package com.ruoyi.app.mendian;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.app.utils.linepay.LinePayClient;
import com.ruoyi.app.utils.linepay.LinePayCredential;
import com.ruoyi.app.utils.linepay.LinePayResponse;
import com.ruoyi.app.pay.LinePayGatewayAuditService;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.PosStoreLinePay;
import com.ruoyi.system.domain.dto.StoreLinePayCredentialDto;
import com.ruoyi.system.domain.dto.StoreLinePayToggleDto;
import com.ruoyi.system.domain.vo.PosStoreLinePayVo;
import com.ruoyi.system.service.IPosStoreLinePayService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Locale;
import java.util.UUID;

/** 平台门店 LINE Pay 凭证版本管理。 */
@RestController
@RequestMapping("/system/storeLinePay")
public class PosStoreLinePayController extends BaseController {

    private final IPosStoreLinePayService credentialService;
    private final LinePayClient linePayClient;
    private final String environment;
    private final LinePayGatewayAuditService auditService;

    public PosStoreLinePayController(IPosStoreLinePayService credentialService,
                                     LinePayClient linePayClient,
                                     @Value("${line-pay.environment:sandbox}") String environment) {
        this(credentialService, linePayClient, environment, null);
    }

    @Autowired
    public PosStoreLinePayController(IPosStoreLinePayService credentialService,
                                     LinePayClient linePayClient,
                                     @Value("${line-pay.environment:sandbox}") String environment,
                                     LinePayGatewayAuditService auditService) {
        this.credentialService = credentialService;
        this.linePayClient = linePayClient;
        this.environment = environment.toUpperCase(Locale.ROOT);
        this.auditService = auditService;
    }

    @PreAuthorize("@ss.hasPermi('chanting:storeLinePay:list')")
    @GetMapping("/list")
    public TableDataInfo list(@RequestParam(required = false) String posName,
                              @RequestParam(required = false) Integer isStall,
                              @RequestParam(required = false) String credentialStatus,
                              @RequestParam(required = false) Integer isEnabled) {
        PosStoreLinePayVo query = new PosStoreLinePayVo();
        query.setPosNameLike(posName);
        query.setIsStall(isStall);
        query.setCredentialStatus(credentialStatus);
        query.setIsEnabled(isEnabled);
        startPage();
        List<PosStoreLinePayVo> rows = credentialService.selectStoreList(query);
        // Mapper list contract deliberately does not select channel_secret.
        return getDataTable(rows);
    }

    @PreAuthorize("@ss.hasPermi('chanting:storeLinePay:query')")
    @GetMapping("/{storeId}")
    public AjaxResult detail(@PathVariable Long storeId) {
        return success(credentialService.selectStoreDetail(storeId));
    }

    @PreAuthorize("@ss.hasPermi('chanting:storeLinePay:saveCredentials')")
    @Log(title = "LINE Pay credential", businessType = BusinessType.UPDATE)
    @PutMapping("/saveCredentials")
    public AjaxResult saveCredentials(@RequestBody StoreLinePayCredentialDto dto) {
        if (dto == null || dto.getStoreId() == null || blank(dto.getChannelId())
                || blank(dto.getChannelSecret())) {
            return error(MessageUtils.message("line.pay.credential.required"));
        }
        LinePayResponse probe;
        try {
            LinePayCredential candidate = new LinePayCredential(null,
                    dto.getChannelId().trim(), dto.getChannelSecret());
            String probeOrderId = credentialProbeOrderId();
            probe = auditService == null
                    ? linePayClient.retrieveByOrderId(candidate, probeOrderId)
                    : auditService.execute("CREDENTIAL_VERIFY", "ADMIN", null, null,
                    null, dto.getStoreId(), null, probeOrderId, null,
                    () -> linePayClient.retrieveByOrderId(candidate, probeOrderId));
        } catch (Exception e) {
            return error(MessageUtils.message("line.pay.credential.verify.unknown"));
        }
        if (!("1150".equals(probe.returnCode()) || validEmptySuccess(probe))) {
            return error(MessageUtils.message("line.pay.credential.invalid"));
        }
        PosStoreLinePay saved = credentialService.saveVerifiedCredential(dto, environment,
                probe.returnCode(), probe.returnMessage());
        return success(MessageUtils.message("line.pay.credential.enabled"), saved);
    }

    @PreAuthorize("@ss.hasPermi('chanting:storeLinePay:toggleEnable')")
    @Log(title = "LINE Pay enable", businessType = BusinessType.UPDATE)
    @PutMapping("/toggleEnable")
    public AjaxResult toggleEnable(@RequestBody StoreLinePayToggleDto dto) {
        if (dto == null || dto.getStoreId() == null || dto.getEnabled() == null) {
            return error(MessageUtils.message("line.pay.credential.required"));
        }
        return toAjax(credentialService.setCurrentEnabled(dto.getStoreId(), dto.getEnabled()));
    }

    private static boolean validEmptySuccess(LinePayResponse response) {
        if (response == null || !response.isSuccess() || response.rawBody() == null) {
            return false;
        }
        Object info = JSONObject.parseObject(response.rawBody()).get("info");
        return info instanceof JSONArray && ((JSONArray) info).isEmpty();
    }

    private static String credentialProbeOrderId() {
        return "LPVERIFY" + UUID.randomUUID().toString().replace("-", "").toUpperCase(Locale.ROOT);
    }

    private static boolean blank(String value) {
        return value == null || value.trim().isEmpty();
    }
}
