package com.ruoyi.app.pay;

import com.ruoyi.app.pay.dto.LinePayOrderRequest;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.PosOrderLinePayment;
import com.ruoyi.system.utils.Auth;
import com.ruoyi.system.utils.JwtUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

/** App and browser entry points for LINE Pay. */
@RestController
@RequestMapping("/pay/line")
public class LinePayController {
    private final LinePayService linePayService;
    private final LinePayAsyncService asyncService;
    private final LinePayReturnPageRenderer pageRenderer;
    private final LinePayGatewayAuditService auditService;

    public LinePayController(LinePayService linePayService,
                             LinePayAsyncService asyncService,
                             LinePayReturnPageRenderer pageRenderer) {
        this(linePayService, asyncService, pageRenderer, null);
    }

    @org.springframework.beans.factory.annotation.Autowired
    public LinePayController(LinePayService linePayService,
                             LinePayAsyncService asyncService,
                             LinePayReturnPageRenderer pageRenderer,
                             LinePayGatewayAuditService auditService) {
        this.linePayService = linePayService;
        this.asyncService = asyncService;
        this.pageRenderer = pageRenderer;
        this.auditService = auditService;
    }

    @Anonymous
    @Auth
    @PostMapping("/create")
    public AjaxResult create(@RequestHeader String token,
                             @RequestBody(required = false) LinePayOrderRequest request) {
        if (request == null || request.getDdId() == null || request.getDdId().trim().isEmpty()) {
            return AjaxResult.error(MessageUtils.message("line.pay.order.required"));
        }
        try {
            Long userId = Long.valueOf(new JwtUtil().getusid(token));
            return AjaxResult.success(linePayService.create(userId, request.getDdId().trim()));
        } catch (Exception exception) {
            return AjaxResult.error(MessageUtils.message("line.pay.operation.failed"));
        }
    }

    @Anonymous
    @Auth
    @PostMapping("/query")
    public AjaxResult query(@RequestHeader String token,
                            @RequestBody(required = false) LinePayOrderRequest request) {
        if (request == null || request.getDdId() == null || request.getDdId().trim().isEmpty()) {
            return AjaxResult.error(MessageUtils.message("line.pay.order.required"));
        }
        try {
            Long userId = Long.valueOf(new JwtUtil().getusid(token));
            return AjaxResult.success(linePayService.query(userId, request.getDdId().trim()));
        } catch (Exception exception) {
            return AjaxResult.error(MessageUtils.message("line.pay.operation.failed"));
        }
    }

    @Anonymous
    @GetMapping("/confirm")
    public ResponseEntity<String> confirm(@RequestParam String orderId,
                                          @RequestParam String transactionId) {
        PosOrderLinePayment payment = linePayService.findByLineOrderId(orderId, transactionId);
        if (payment != null) {
            auditReturn(payment, "REDIRECT_CONFIRM");
            schedule(payment, "CONFIRM_RETURN");
        }
        return html(payment == null ? null : payment.getDdId());
    }

    @Anonymous
    @GetMapping("/cancel")
    public ResponseEntity<String> cancel(
            @RequestParam(required = false) String orderId,
            @RequestParam(required = false) String transactionId) {
        PosOrderLinePayment payment = orderId == null ? null
                : linePayService.findByLineOrderId(orderId, transactionId);
        if (payment != null) {
            auditReturn(payment, "REDIRECT_CANCEL");
            schedule(payment, "CANCEL_RETURN");
        }
        return html(payment == null ? null : payment.getDdId());
    }

    private ResponseEntity<String> html(String ddId) {
        String nonce = UUID.randomUUID().toString().replace("-", "");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(new MediaType("text", "html", StandardCharsets.UTF_8));
        headers.set("Cache-Control", "no-store, no-cache, must-revalidate");
        headers.set("Pragma", "no-cache");
        headers.set("Referrer-Policy", "no-referrer");
        headers.set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; "
                + "script-src 'nonce-" + nonce + "'; img-src data:; base-uri 'none'; "
                + "form-action 'none'; frame-ancestors 'none'");
        headers.set("X-Content-Type-Options", "nosniff");
        return ResponseEntity.ok().headers(headers).body(pageRenderer.render(ddId, nonce));
    }

    private void schedule(PosOrderLinePayment payment, String source) {
        try {
            asyncService.reconcile(payment.getId(), source);
        } catch (RuntimeException ignored) {
            // The durable scheduled reconciler will process the already-persisted payment intent.
        }
    }

    private void auditReturn(PosOrderLinePayment payment, String action) {
        if (auditService != null) {
            auditService.event(action, "BROWSER", payment.getId(), payment.getCredentialId(),
                    payment.getStoreId(), payment.getDdId(), payment.getLineOrderId(),
                    payment.getTransactionId());
        }
    }
}
