package com.ga.api.service.withdraw;

import com.ga.api.service.push.PushDispatchService;
import com.ga.common.adapter.bank.BankTransferAdapter;
import com.ga.common.adapter.bank.BankTransferResponse;
import com.ga.common.adapter.bank.BankTransferStatus;
import com.ga.core.mapper.notice.UserNotificationMapper;
import com.ga.core.mapper.withdraw.WithdrawRequestMapper;
import com.ga.core.vo.notice.NotificationRefType;
import com.ga.core.vo.notice.UserNotificationVO;
import com.ga.core.vo.withdraw.WithdrawRequestVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 펌뱅킹 SETTLED 동기화 서비스.
 * <p>
 * 어댑터가 ACCEPTED(접수)만 반환한 출금 건들에 대해 {@code queryStatus} 폴링으로 입금 확정 여부를 확인.
 * <ul>
 *   <li>SETTLED → status=COMPLETED, transfer_status=SETTLED, completed_at=NOW()</li>
 *   <li>FAILED  → status=APPROVED, transfer_status=FAILED (재시도 풀로 회귀)</li>
 *   <li>ACCEPTED 유지 → no-op (다음 사이클에서 다시 폴링)</li>
 * </ul>
 * <p>운영: {@code POST /api/internal/bank-settle} 수동 트리거 + cron 옵션(기본 비활성).
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WithdrawBankSettleService {

    private static final int DEFAULT_BATCH_LIMIT = 50;

    private final WithdrawRequestMapper mapper;
    private final BankTransferAdapter bankTransferAdapter;
    private final UserNotificationMapper notificationMapper;
    private final PushDispatchService pushDispatchService;

    @Value("${app.bank-settle.limit:50}")
    private int batchLimit;

    /** 동기화 1회 — 상태 전이된 건수 반환(SETTLED + FAILED 합산). */
    @Transactional
    public int syncOnce() {
        int limit = batchLimit > 0 ? batchLimit : DEFAULT_BATCH_LIMIT;
        List<WithdrawRequestVO> targets = mapper.selectAcceptedTransfers(limit);
        if (targets.isEmpty()) return 0;

        log.info("[BANK-SETTLE] 대상 {}건 동기화 시작", targets.size());
        int changed = 0;
        for (WithdrawRequestVO vo : targets) {
            String txId = vo.getTransferTxId();
            if (txId == null) continue;
            try {
                BankTransferResponse resp = bankTransferAdapter.queryStatus(txId);
                BankTransferStatus status = resp != null ? resp.getStatus() : null;

                if (status == BankTransferStatus.SETTLED) {
                    int n = mapper.markSettled(vo.getRequestId());
                    if (n > 0) {
                        changed++;
                        notify(vo, true, null);
                    }
                } else if (status == BankTransferStatus.FAILED) {
                    String reason = resp.getResultMessage() != null ? resp.getResultMessage() : "settle failed";
                    int n = mapper.markSettleFailed(vo.getRequestId(), reason);
                    if (n > 0) {
                        changed++;
                        notify(vo, false, reason);
                    }
                }
                // ACCEPTED 유지 시 no-op
            } catch (Exception e) {
                log.error("[BANK-SETTLE] queryStatus 어댑터 예외: requestId={} txId={}",
                        vo.getRequestId(), txId, e);
            }
        }
        log.info("[BANK-SETTLE] 완료 — 상태전이 {}/{}건", changed, targets.size());
        return changed;
    }

    /**
     * 신청자에게 결과 알림. 본인 알림이 실패해도 동기화 흐름은 비차단.
     */
    private void notify(WithdrawRequestVO vo, boolean settled, String reason) {
        Long userId = vo.getRequestedBy();
        if (userId == null) return;
        try {
            UserNotificationVO n = new UserNotificationVO();
            n.setUserId(userId);
            n.setRefType(NotificationRefType.WITHDRAW.name());
            n.setRefId(vo.getRequestId());
            n.setIsRead(false);
            if (settled) {
                n.setTitle("출금 입금 완료");
                n.setContent("#" + vo.getRequestId() + " 출금 신청이 입금 완료되었습니다 ("
                        + vo.getAmount() + "원).");
            } else {
                n.setTitle("출금 입금 실패");
                n.setContent("#" + vo.getRequestId() + " 출금이 실패하였습니다"
                        + (reason != null ? " — " + reason : "")
                        + ". 재시도 처리됩니다.");
            }
            notificationMapper.insert(n);
            // 인앱 알림과 함께 디바이스 푸시도 best-effort 발송
            pushDispatchService.sendToUser(userId, n.getTitle(), n.getContent(),
                    NotificationRefType.WITHDRAW.name(), vo.getRequestId());
        } catch (Exception e) {
            log.warn("[BANK-SETTLE] 알림 발행 실패 requestId={}: {}", vo.getRequestId(), e.getMessage());
        }
    }

    /**
     * cron 기반 자동 동기화.
     * yml 에 {@code app.bank-settle.cron} 미설정 시 매년 1월 1일 0시(=사실상 비활성)로 작동.
     */
    @Scheduled(cron = "${app.bank-settle.cron:0 0 0 1 1 ?}")
    public void scheduled() {
        try {
            syncOnce();
        } catch (Exception e) {
            log.error("[BANK-SETTLE] 스케줄러 실패", e);
        }
    }
}
