package study.gateway.proxymvc;

import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.mvc.ProxyExchange;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@SpringBootApplication
@RestController
public class ProxyMvcApplication {
    private final URI upstream;

    public ProxyMvcApplication(@Value("${lab.upstream}") URI upstream) { this.upstream = upstream; }

    public static void main(String[] args) { SpringApplication.run(ProxyMvcApplication.class, args); }

    @RequestMapping(path = "/proxy/**", method = {RequestMethod.GET, RequestMethod.POST})
    ResponseEntity<byte[]> proxy(ProxyExchange<byte[]> proxy, HttpServletRequest request) {
        proxy.uri(target(proxy.path("/proxy/"), request.getQueryString()))
                .header("X-Before", "proxy-mvc");
        return "POST".equals(request.getMethod()) ? proxy.post(this::response) : proxy.get(this::response);
    }

    @PostMapping("/replace/**")
    ResponseEntity<byte[]> replace(ProxyExchange<byte[]> proxy, @RequestBody String body) {
        return proxy.uri(target(proxy.path("/replace/"), null)).body("new:" + body).post(this::response);
    }

    @GetMapping("/dispatch")
    void dispatch(ProxyExchange<byte[]> proxy) { proxy.forward("/local"); }

    @GetMapping("/local")
    String local(HttpServletRequest request) { return "local:" + request.getDispatcherType(); }

    @GetMapping("/bad-prefix/**")
    String badPrefix(ProxyExchange<byte[]> proxy) { return proxy.path("/different/"); }

    @ExceptionHandler(IllegalArgumentException.class)
    ResponseEntity<String> badInput() { return ResponseEntity.badRequest().body("Invalid proxy path"); }

    private String target(String tail, String query) {
        if (tail == null || !tail.matches("[A-Za-z0-9/_-]+")) {
            throw new IllegalArgumentException("Only simple fixture paths are accepted");
        }
        // Origin is configuration, never a caller-supplied URL. Query must already be URI encoded.
        return upstream + "/api/" + tail + (query == null ? "" : "?" + query);
    }

    private ResponseEntity<byte[]> response(ResponseEntity<byte[]> result) {
        return ResponseEntity.status(result.getStatusCode()).headers(result.getHeaders())
                .header("X-Proxy", "proxy-mvc").body(result.getBody());
    }
}
