package com.zioinfo.mall.payment;

import com.zioinfo.mall.admin.AuditService;
import com.zioinfo.mall.common.CryptoUtil;
import com.zioinfo.mall.order.MallOrder;
import com.zioinfo.mall.order.OrderService;
import com.zioinfo.mall.order.mapper.OrderMapper;
import com.zioinfo.mall.payment.mapper.PaymentMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Random;
import java.util.UUID;

/**
 * 결제 서비스 — 모의 PG / 가상계좌.
 *
 * <p>보안: 카드/계좌 번호는 {@link CryptoUtil} 로 암호화 저장하고, 응답 시 마스킹만 노출(redact()).
 * 모의 PG 는 항상 승인(데모) — pgTid 발급. VBANK 는 가상계좌 번호 발급 후 READY.
 */
@Service
@RequiredArgsConstructor
public class PaymentService {

    private final PaymentMapper paymentMapper;
    private final OrderMapper orderMapper;
    private final OrderService orderService;
    private final CryptoUtil crypto;
    private final AuditService auditService;

    /** 결제 요청 — 주문 소유자만. CARD: 즉시 승인→주문 PAID. VBANK: 가상계좌 발급(READY). */
    @Transactional
    public MallPayment pay(String owner, boolean admin, PayRequest req) {
        MallOrder order = orderService.detail(req.orderId(), owner, admin);
        if (!"PENDING".equals(order.getStatus())) {
            throw new RuntimeException("ERR-PAY-409: 결제 가능한 주문 상태가 아닙니다");
        }
        MallPayment p = new MallPayment();
        p.setOrderId(order.getId());
        p.setAmount(order.getPayAmount());
        String method = req.method() == null ? "CARD" : req.method().toUpperCase();
        p.setMethod(method);
        if (req.cardNo() != null && !req.cardNo().isBlank()) {
            p.setCardNoEnc(crypto.encrypt(req.cardNo()));
        }
        if (req.accountNo() != null && !req.accountNo().isBlank()) {
            p.setAccountNoEnc(crypto.encrypt(req.accountNo()));
        }
        if ("VBANK".equals(method)) {
            p.setStatus("READY");
            p.setVbankBank(req.bank() == null ? "지오은행" : req.bank());
            p.setVbankNo(generateVbank());
            paymentMapper.insert(p);
        } else {
            // 모의 PG 즉시 승인
            p.setStatus("PAID");
            p.setPgTid("PG-" + UUID.randomUUID().toString().substring(0, 12).toUpperCase());
            paymentMapper.insert(p);
            orderService.transition(order.getId(), owner, true, "PAID");
        }
        auditService.log(owner, "PAYMENT", String.valueOf(order.getOrderNo()),
                method + " " + p.getStatus() + " amount=" + p.getAmount());
        return redact(paymentMapper.findById(p.getId()));
    }

    /** 가상계좌 입금 확인(모의) — READY → PAID + 주문 PAID. */
    @Transactional
    public MallPayment confirmVbank(String owner, boolean admin, Long paymentId) {
        MallPayment p = paymentMapper.findById(paymentId);
        if (p == null) {
            throw new RuntimeException("ERR-PAY-404: 결제 정보 없음");
        }
        if (!"READY".equals(p.getStatus())) {
            throw new RuntimeException("ERR-PAY-409: 입금 대기 상태가 아닙니다");
        }
        paymentMapper.updateStatus(paymentId, "PAID");
        orderService.transition(p.getOrderId(), owner, true, "PAID");
        auditService.log(owner, "PAYMENT_VBANK_CONFIRM", String.valueOf(p.getOrderId()), "PAID");
        return redact(paymentMapper.findById(paymentId));
    }

    public MallPayment getByOrder(Long orderId, String owner, boolean admin) {
        // 주문 소유 검증
        orderService.detail(orderId, owner, admin);
        MallPayment p = paymentMapper.findByOrderId(orderId);
        return p == null ? null : redact(p);
    }

    /** 민감 필드 마스킹 — card_no_enc/account_no_enc 는 절대 응답에 포함하지 않는다. */
    private MallPayment redact(MallPayment p) {
        if (p.getCardNoEnc() != null && !p.getCardNoEnc().isEmpty()) {
            p.setCardMasked(CryptoUtil.maskTail(crypto.decrypt(p.getCardNoEnc())));
        }
        if (p.getAccountNoEnc() != null && !p.getAccountNoEnc().isEmpty()) {
            p.setAccountMasked(CryptoUtil.maskTail(crypto.decrypt(p.getAccountNoEnc())));
        }
        p.setCardNoEnc(null);
        p.setAccountNoEnc(null);
        return p;
    }

    private String generateVbank() {
        Random r = new Random();
        return String.format("%03d-%04d-%06d", r.nextInt(1000), r.nextInt(10000), r.nextInt(1000000));
    }

    public record PayRequest(Long orderId, String method, String cardNo, String accountNo, String bank) {}
}
