package com.honey.honey.service;

import com.honey.honey.dto.WithdrawalInfoApiResponse;
import com.honey.honey.model.Payout;
import com.honey.honey.repository.PayoutRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.PageRequest;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * Cron job: once per minute, fetches PROCESSING payouts (with payment_id), checks status
 * via GET api/v1/withdrawals-info/{payment_id}, and updates DB (status, updated_at, and
 * for COMPLETED/CANCELLED the same logic as admin complete/cancel).
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WithdrawalStatusSyncService {

    private static final int BATCH_SIZE = 20;

    private final PayoutRepository payoutRepository;
    private final CryptoWithdrawalService cryptoWithdrawalService;
    private final PayoutService payoutService;

    @Scheduled(cron = "0 * * * * *") // Every minute at second 0
    @Transactional
    public void syncWithdrawalStatuses() {
        List<Payout> payouts = payoutRepository.findByTypeAndStatusInAndPaymentIdIsNotNullOrderByUpdatedAtAsc(
                Payout.PayoutType.CRYPTO,
                Set.of(Payout.PayoutStatus.PROCESSING, Payout.PayoutStatus.WAITING),
                PageRequest.of(0, BATCH_SIZE)
        );
        log.info("Withdrawal sync cron: starting, {} CRYPTO payout(s) to process", payouts.size());
        if (payouts.isEmpty()) {
            return;
        }
        Instant now = Instant.now();

        for (Payout payout : payouts) {
            Integer paymentId = payout.getPaymentId();
            try {
                if (paymentId == null) {
                    log.warn("Withdrawal sync: skipping payout id={} (payment_id is null)", payout.getId());
                    continue;
                }
                log.debug("Withdrawal sync: checking payout id={}, paymentId={}, status={}", payout.getId(), paymentId, payout.getStatus());
                Optional<WithdrawalInfoApiResponse> infoOpt = cryptoWithdrawalService.getWithdrawalInfo(paymentId);
                if (infoOpt.isEmpty() || infoOpt.get().getResult() == null
                        || infoOpt.get().getResult().getPaymentList() == null
                        || infoOpt.get().getResult().getPaymentList().isEmpty()) {
                    log.warn("Withdrawal info empty or no payment_list: payoutId={}, paymentId={}", payout.getId(), paymentId);
                    payout.setUpdatedAt(now);
                    payoutRepository.save(payout);
                    continue;
                }
                WithdrawalInfoApiResponse.PaymentItem paymentItem = infoOpt.get().getResult().getPaymentList().get(0);
                String apiStatus = paymentItem.getStatus();
                if (paymentItem.getTxhash() != null && !paymentItem.getTxhash().isBlank()) {
                    payout.setTxhash(paymentItem.getTxhash());
                }
                Payout.PayoutStatus newStatus = mapApiStatusToPayoutStatus(apiStatus);
                if (newStatus == null) {
                    log.warn("Unknown withdrawal API status: payoutId={}, paymentId={}, status={}", payout.getId(), paymentId, apiStatus);
                    payout.setUpdatedAt(now);
                    payoutRepository.save(payout);
                    continue;
                }

                switch (newStatus) {
                    case PROCESSING:
                        payout.setUpdatedAt(now);
                        payoutRepository.save(payout);
                        log.debug("Withdrawal sync: payout id={} still PROCESSING, updated_at refreshed", payout.getId());
                        break;
                    case WAITING:
                        payout.setStatus(Payout.PayoutStatus.WAITING);
                        payout.setUpdatedAt(now);
                        payoutRepository.save(payout);
                        log.debug("Withdrawal sync: payout id={}, paymentId={} -> WAITING", payout.getId(), paymentId);
                        break;
                    case COMPLETED:
                        payoutRepository.save(payout); // persist txhash if set
                        payoutService.markPayoutCompleted(payout.getId());
                        log.debug("Withdrawal sync: payout id={}, paymentId={} -> COMPLETED", payout.getId(), paymentId);
                        break;
                    case CANCELLED:
                        payoutRepository.save(payout); // persist txhash if set
                        payoutService.markPayoutCancelled(payout.getId());
                        log.debug("Withdrawal sync: payout id={}, paymentId={} -> CANCELLED", payout.getId(), paymentId);
                        break;
                    default:
                        payout.setUpdatedAt(now);
                        payoutRepository.save(payout);
                }
            } catch (Exception e) {
                log.warn("Withdrawal sync error for payout id={}, paymentId={}: {}", payout.getId(), paymentId, e.getMessage(), e);
                // Still update updated_at so we don't block the batch forever on one bad record
                payout.setUpdatedAt(now);
                payoutRepository.save(payout);
            }
        }
        log.debug("Withdrawal sync cron: finished");
    }

    /** -1 PROCESSING, 0 WAITING, 1 COMPLETED, 2 CANCELLED */
    private static Payout.PayoutStatus mapApiStatusToPayoutStatus(String status) {
        if (status == null) {
            return null;
        }
        return switch (status.trim()) {
            case "-1" -> Payout.PayoutStatus.PROCESSING;
            case "0" -> Payout.PayoutStatus.WAITING;
            case "1" -> Payout.PayoutStatus.COMPLETED;
            case "2" -> Payout.PayoutStatus.CANCELLED;
            default -> null;
        };
    }
}
