package com.honey.honey.controller;

import com.honey.honey.dto.CryptoWithdrawalResponse;
import com.honey.honey.dto.CreateCryptoWithdrawalRequest;
import com.honey.honey.dto.CreatePaymentRequest;
import com.honey.honey.dto.DepositAddressRequest;
import com.honey.honey.dto.DepositAddressResultDto;
import com.honey.honey.dto.DepositMethodsDto;
import com.honey.honey.dto.ErrorResponse;
import com.honey.honey.dto.PaymentInvoiceResponse;
import com.honey.honey.model.Payout;
import com.honey.honey.model.UserA;
import com.honey.honey.security.UserContext;
import com.honey.honey.dto.WithdrawalMethodDetailsDto;
import com.honey.honey.dto.WithdrawalMethodsDto;
import com.honey.honey.service.CryptoDepositService;
import com.honey.honey.service.CryptoWithdrawalService;
import com.honey.honey.service.FeatureSwitchService;
import com.honey.honey.service.LocalizationService;
import com.honey.honey.service.PaymentService;
import com.honey.honey.service.PayoutService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/payments")
@RequiredArgsConstructor
public class PaymentController {

    private final PaymentService paymentService;
    private final CryptoDepositService cryptoDepositService;
    private final CryptoWithdrawalService cryptoWithdrawalService;
    private final PayoutService payoutService;
    private final FeatureSwitchService featureSwitchService;
    private final LocalizationService localizationService;

    /**
     * Returns minimum deposit from DB only (no sync). Used by Store screen for validation.
     */
    @GetMapping("/minimum-deposit")
    public ResponseEntity<?> getMinimumDeposit() {
        if (!featureSwitchService.isPaymentEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable")));
        }
        return ResponseEntity.ok(java.util.Map.of("minimumDeposit", cryptoDepositService.getMinimumDeposit()));
    }

    /**
     * Returns crypto deposit methods and minimum_deposit from DB only (sync is done every 10 min).
     * Called when user opens Payment Options screen.
     */
    @GetMapping("/deposit-methods")
    public ResponseEntity<?> getDepositMethods() {
        if (!featureSwitchService.isPaymentEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable")));
        }
        DepositMethodsDto dto = cryptoDepositService.getDepositMethodsFromDb();
        return ResponseEntity.ok(dto);
    }

    /**
     * Returns crypto withdrawal methods from DB only (sync is done every 30 min).
     * Called when user opens Payout screen.
     */
    @GetMapping("/withdrawal-methods")
    public ResponseEntity<?> getWithdrawalMethods() {
        if (!featureSwitchService.isPayoutEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable")));
        }
        WithdrawalMethodsDto dto = cryptoWithdrawalService.getWithdrawalMethodsFromDb();
        return ResponseEntity.ok(dto);
    }

    /**
     * Returns withdrawal method details (rate_usd, misha_fee_usd) from external API for the given pid.
     * Called when user opens Payout Confirmation screen to show network fee and compute "You will receive".
     */
    @GetMapping("/withdrawal-method-details")
    public ResponseEntity<?> getWithdrawalMethodDetails(@RequestParam("pid") int pid) {
        if (!featureSwitchService.isPayoutEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable")));
        }
        return cryptoWithdrawalService.getWithdrawalMethodDetails(pid)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    /**
     * Creates a crypto withdrawal: calls external API, then on success creates payout and deducts balance.
     * Uses in-memory lock to prevent double-submit. Validates deposit total and maxWinAfterDeposit.
     */
    @PostMapping("/crypto-withdrawal")
    public ResponseEntity<?> createCryptoWithdrawal(@RequestBody CreateCryptoWithdrawalRequest request) {
        if (!featureSwitchService.isPayoutEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.payoutsUnavailable")));
        }
        try {
            UserA user = UserContext.get();
            if (user == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("Authentication required"));
            }
            Payout payout = payoutService.createCryptoPayout(user.getId(), request);
            return ResponseEntity.ok(CryptoWithdrawalResponse.builder()
                    .id(payout.getId())
                    .status(payout.getStatus().name())
                    .build());
        } catch (IllegalArgumentException e) {
            log.warn("Crypto withdrawal validation failed: {}", e.getMessage());
            return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
        } catch (IllegalStateException e) {
            log.warn("Crypto withdrawal failed: {}", e.getMessage());
            return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
        } catch (Exception e) {
            log.error("Crypto withdrawal error: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ErrorResponse("Withdrawal failed. Please try again later."));
        }
    }

    /**
     * Creates a payment invoice for the current user.
     * Returns invoice data that frontend will use to open Telegram payment UI.
     */
    @PostMapping("/create")
    public ResponseEntity<?> createPaymentInvoice(@RequestBody CreatePaymentRequest request) {
        if (!featureSwitchService.isPaymentEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable")));
        }
        try {
            UserA user = UserContext.get();
            PaymentInvoiceResponse response = paymentService.createPaymentInvoice(user.getId(), request);
            return ResponseEntity.ok(response);
        } catch (IllegalArgumentException e) {
            log.warn("Payment invoice creation failed: {}", e.getMessage());
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse(e.getMessage()));
        } catch (Exception e) {
            log.error("Payment invoice creation error: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ErrorResponse("Failed to create payment invoice: " + e.getMessage()));
        }
    }


    /**
     * Gets a crypto deposit address from the external API (no payment record is created).
     * Call when user selects a payment method on Payment Options screen.
     * Returns address, amount_coins, name, network for the Payment Confirmation screen.
     */
    @PostMapping("/deposit-address")
    public ResponseEntity<?> getDepositAddress(@RequestBody DepositAddressRequest request) {
        if (!featureSwitchService.isPaymentEnabled()) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                    .body(new ErrorResponse(localizationService.getMessage("feature.depositsUnavailable")));
        }
        try {
            UserA user = UserContext.get();
            if (user == null) {
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(new ErrorResponse("Authentication required"));
            }
            DepositAddressResultDto result = paymentService.requestCryptoDepositAddress(
                    user.getId(), request.getPid(), request.getUsdAmount());
            return ResponseEntity.ok(result);
        } catch (IllegalArgumentException e) {
            log.warn("Deposit address request failed: {}", e.getMessage());
            return ResponseEntity.badRequest().body(new ErrorResponse(e.getMessage()));
        } catch (Exception e) {
            log.error("Deposit address error: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ErrorResponse(e.getMessage() != null ? e.getMessage() : "Failed to get deposit address"));
        }
    }

    /**
     * Cancels a payment (e.g., when user cancels in Telegram UI).
     */
    @PostMapping("/cancel")
    public ResponseEntity<?> cancelPayment(@RequestBody CancelPaymentRequest request) {
        try {
            String orderId = request.getOrderId();
            UserA caller = UserContext.get();
            log.info("Payment cancel requested: orderId={}, callerUserId={}", orderId, caller != null ? caller.getId() : null);
            paymentService.cancelPayment(orderId);
            return ResponseEntity.ok().body(new PaymentWebhookResponse(true, "Payment cancelled"));
        } catch (IllegalArgumentException e) {
            log.warn("Payment cancellation failed: {}", e.getMessage());
            return ResponseEntity.badRequest()
                    .body(new ErrorResponse(e.getMessage()));
        } catch (Exception e) {
            log.error("Payment cancellation error: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(new ErrorResponse("Failed to cancel payment: " + e.getMessage()));
        }
    }

    // Response DTOs
    private static class PaymentWebhookResponse {
        private final boolean success;
        private final String message;

        public PaymentWebhookResponse(boolean success, String message) {
            this.success = success;
            this.message = message;
        }

        public boolean isSuccess() {
            return success;
        }

        public String getMessage() {
            return message;
        }
    }

    private static class CancelPaymentRequest {
        private String orderId;

        public String getOrderId() {
            return orderId;
        }

        public void setOrderId(String orderId) {
            this.orderId = orderId;
        }
    }
}

