package ru.shine.promo.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import ru.shine.promo.config.AppProperties;
import ru.shine.promo.dto.PromoRequest;
import ru.shine.promo.dto.PromoResponse;
import ru.shine.promo.service.PromoCodeService;
import ru.shine.promo.service.PromoException;
import ru.shine.promo.service.PromoTransferService;
import ru.shine.promo.service.UsedPromoStorageService;

import java.util.Map;

@RestController
public class PromoApiController {

    private static final Logger log = LoggerFactory.getLogger(PromoApiController.class);

    private final AppProperties appProperties;
    private final PromoCodeService promoCodeService;
    private final PromoTransferService promoTransferService;
    private final UsedPromoStorageService usedPromoStorageService;

    public PromoApiController(
            AppProperties appProperties,
            PromoCodeService promoCodeService,
            PromoTransferService promoTransferService,
            UsedPromoStorageService usedPromoStorageService
    ) {
        this.appProperties = appProperties;
        this.promoCodeService = promoCodeService;
        this.promoTransferService = promoTransferService;
        this.usedPromoStorageService = usedPromoStorageService;
    }

    @PostMapping("/api/promo/top-up")
    public ResponseEntity<PromoResponse> topUp(@RequestBody PromoRequest request) {
        String wallet = trimToEmpty(request == null ? null : request.getWallet());
        String name = trimToEmpty(request == null ? null : request.getName());
        String promoCode = promoCodeService.normalizePromoCode(request == null ? null : request.getPromoCode());

        if (wallet.isEmpty()) {
            return error(HttpStatus.BAD_REQUEST, "Пустой wallet");
        }
        if (!promoTransferService.isSolanaWalletValid(wallet)) {
            return error(HttpStatus.BAD_REQUEST, "Неверный формат Solana-адреса");
        }
        if (name.isEmpty()) {
            return error(HttpStatus.BAD_REQUEST, "Пустое имя");
        }
        if (name.length() < 2 || name.length() > 120) {
            return error(HttpStatus.BAD_REQUEST, "Имя должно быть длиной от 2 до 120 символов");
        }
        if (promoCode.isEmpty()) {
            return error(HttpStatus.BAD_REQUEST, "Пустой промокод");
        }
        boolean eternalPromo = promoCodeService.isEternalPromoCode(promoCode);
        if (!eternalPromo && !promoCodeService.isPromoCodeFormatValid(promoCode)) {
            return error(HttpStatus.BAD_REQUEST, "Неверный формат промокода");
        }

        try {
            if (!eternalPromo && !promoCodeService.promoCodeExists(promoCode)) {
                return error(HttpStatus.BAD_REQUEST, "Промокод не найден");
            }

            String signature = usedPromoStorageService.executeLocked(() -> {
                if (!eternalPromo && usedPromoStorageService.isPromoUsed(promoCode)) {
                    throw new PromoException(HttpStatus.CONFLICT, "Промокод уже использован");
                }

                String txSignature = promoTransferService.sendPromoTransfer(wallet);
                // Для вечного промокода ведём только лог использования, но не блокируем повторное применение.
                usedPromoStorageService.appendUsedPromo(promoCode, wallet, name, txSignature);
                return txSignature;
            });

            String explorerUrl = promoTransferService.buildExplorerUrl(signature);
            String amount = appProperties.getPromoTransferAmountSol().stripTrailingZeros().toPlainString();

            log.info(
                    "Promo top-up success: wallet={}, name={}, promoCode={}, signature={}",
                    wallet,
                    name,
                    promoCode,
                    signature
            );

            PromoResponse response = PromoResponse.success(
                    "Тестовое пополнение выполнено",
                    wallet,
                    name,
                    amount,
                    signature,
                    explorerUrl
            );
            return ResponseEntity.ok(response);
        } catch (PromoException e) {
            if (e.getStatus().is5xxServerError()) {
                log.error("Top-up failed: {}", e.getMessage(), e);
            } else {
                log.warn("Top-up rejected: {}", e.getMessage());
            }
            return error(e.getStatus(), e.getMessage());
        } catch (Exception e) {
            log.error("Unexpected top-up error", e);
            return error(HttpStatus.INTERNAL_SERVER_ERROR, "Внутренняя ошибка сервера");
        }
    }

    @GetMapping("/health")
    public Map<String, String> health() {
        return Map.of(
                "status", "ok",
                "app", "SHiNE-promo-solana-devnet"
        );
    }

    private ResponseEntity<PromoResponse> error(HttpStatus status, String message) {
        return ResponseEntity.status(status).body(PromoResponse.error(message));
    }

    private String trimToEmpty(String value) {
        if (value == null) {
            return "";
        }
        return value.trim().replaceAll("\\s{2,}", " ");
    }
}
