package ru.soune.nocopy.service.payment;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.ReferralRepo;
import ru.soune.ReferralService;
import ru.soune.nocopy.client.YooKassaClient;
import ru.soune.nocopy.dto.payment.PaymentMethodDTO;
import ru.soune.nocopy.entity.notification.NotificationType;
import ru.soune.nocopy.entity.payment.Payment;
import ru.soune.nocopy.entity.payment.PaymentMethod;
import ru.soune.nocopy.entity.payment.PaymentOperationType;
import ru.soune.nocopy.entity.payment.PaymentStatus;
import ru.soune.nocopy.entity.tarif.Tariff;
import ru.soune.nocopy.entity.tarif.TariffInfo;
import ru.soune.nocopy.entity.tarif.TariffStatus;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.PaymentNotFoundException;
import ru.soune.nocopy.exception.TariffNotFoundException;
import ru.soune.nocopy.exception.UserNotFoundException;
import ru.soune.nocopy.repository.*;
import ru.soune.nocopy.service.notification.NotificationService;
import ru.soune.nocopy.service.tariff.TariffInfoService;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class PaymentService {
    private final PaymentRepository paymentRepository;

    private final UserRepository userRepository;

    private final TariffRepository tariffRepository;

    private final TariffInfoRepository tariffInfoRepository;

    private final TariffInfoService tariffInfoService;

    private final ReferralService referralService;

    private final PaymentMethodRepository paymentMethodRepository;

    private final YooKassaClient yooKassaClient;

    private final ReferralRepo referralRepo;

    private final NotificationService notificationService;

    @Transactional
    public Payment createPayment(String email, Long tariffId, String operationType, String paymentUuid) {
        User user = userRepository.findByEmail(email);

        if (user == null) {
            throw new UserNotFoundException(email);
        }

        Tariff tariff = tariffRepository.findById(tariffId).orElseThrow(() ->
                new TariffNotFoundException("Tariff not found"));

        Payment payment = new Payment();
        payment.setStatus(PaymentStatus.PENDING);
        payment.setAmount(tariff.getPrice());
        payment.setOperationType(PaymentOperationType.valueOf(operationType));
        payment.setUser(user);
        payment.setTariff(tariff);
        payment.setPaymentUuid(paymentUuid);

        return paymentRepository.save(payment);
    }

    private void savePaymentMethod(User user, Map<String, Object> paymentMethodData) {
        if (paymentMethodData == null) return;

        String paymentMethodId = (String) paymentMethodData.get("id");
        if (paymentMethodRepository.findByPaymentMethodId(paymentMethodId).isPresent()) return;

        Map<String, Object> card = (Map<String, Object>) paymentMethodData.get("card");
        if (card == null) return;

        PaymentMethod pm = new PaymentMethod();
        pm.setUser(user);
        pm.setPaymentMethodId(paymentMethodId);
        pm.setLastFour((String) card.get("last4"));
        pm.setCardType((String) card.get("card_type"));
        pm.setExpiryMonth((String) card.get("expiry_month"));
        pm.setExpiryYear((String) card.get("expiry_year"));
        pm.setCreatedAt(LocalDateTime.now());

        paymentMethodRepository.save(pm);
    }

    @Transactional(readOnly = true)
    public List<PaymentMethodDTO> getUserPaymentMethods(Long userId) {
        User user = userRepository.findById(userId).orElseThrow();

        return paymentMethodRepository.findByUserAndActiveTrue(user)
                .stream()
                .map(pm -> new PaymentMethodDTO(
                        pm.getPaymentMethodId(),
                        pm.getLastFour(),
                        pm.getCardType(),
                        pm.getExpiryMonth(),
                        pm.getExpiryYear()
                ))
                .collect(Collectors.toList());
    }

    @Transactional
    public void deletePaymentMethod(Long userId, String paymentMethodId) {
        User user = userRepository.findById(userId).orElseThrow();
        PaymentMethod pm = paymentMethodRepository.findByPaymentMethodId(paymentMethodId)
                .orElseThrow(() -> new RuntimeException("Payment method not found"));

        if (!pm.getUser().getId().equals(user.getId())) {
            throw new RuntimeException("Access denied");
        }

        pm.setActive(false);
        paymentMethodRepository.save(pm);
    }

    @Transactional
    public void changeAutoRenewal(Long userId, boolean autoRenewal) {
        User user = userRepository.findById(userId).orElseThrow();

        TariffInfo tariffInfo = user.getActiveTariffInfo();
        if (tariffInfo != null) {
            tariffInfo.setAutoRenewal(autoRenewal);
            tariffInfoRepository.save(tariffInfo);
        }
    }

    @Transactional
    public void handlePaymentNotification(Map<String, Object> notification) {
        String eventType = (String) notification.get("event");
        Map<String, Object> object = (Map<String, Object>) notification.get("object");
        String paymentUuid = (String) object.get("id");

        Payment payment = paymentRepository.findByPaymentUuid(paymentUuid)
                .orElseThrow(() -> new PaymentNotFoundException("Payment not found"));

        if ("payment.succeeded".equals(eventType)) {
            Tariff tariff = payment.getTariff();
            double price = tariff.getPrice();
            int intPrice = (int) Math.round(price);

            payment.setStatus(PaymentStatus.SUCCEEDED);
            payment.setCancellationReason(null);

            Map<String, String> metadata = (Map<String, String>) object.get("metadata");
            Map<String, Object> paymentMethod = (Map<String, Object>) object.get("payment_method");
            String paymentMethodId = paymentMethod != null ? (String) paymentMethod.get("id") : null;

            boolean autoRenewal = metadata != null && Boolean.parseBoolean(metadata.get("auto_renewal"));

            activateTariffForUser(payment.getUser(), tariff, autoRenewal, paymentMethodId);

            savePaymentMethod(payment.getUser(), paymentMethod);

            referralService.onUserAccountRefill(payment.getUser().getId(), intPrice);

//            long inviterIdForUser = referralRepo.getInviterIdForUser(payment.getUser().getId());

//            notificationService.addNotification(NotificationType.REFERRAL_ACTIVATED, inviterIdForUser);
        } else if ("payment.canceled".equals(eventType)) {
            payment.setStatus(PaymentStatus.CANCELED);

            Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");

            String reason = (String) cancellationDetails.get("reason");
            payment.setCancellationReason(reason);

            notificationService.addNotification(NotificationType.PAYMENT_RESULT, payment.getUser().getId(), reason);
        } else if ("payment.waiting_for_capture".equals(eventType)) {
            payment.setStatus(PaymentStatus.WAITING);
            payment.setCancellationReason(null);

        } else {
            payment.setStatus(PaymentStatus.FAILED);

            Map<String, Object> cancellationDetails = (Map<String, Object>) object.get("cancellation_details");

            String reason = (String) cancellationDetails.get("reason");
            payment.setCancellationReason(reason);

            notificationService.addNotification(NotificationType.PAYMENT_RESULT, payment.getUser().getId(), reason);
        }

        paymentRepository.save(payment);
    }

    public List<Payment> userPayments(String email) {
        User user = userRepository.findByEmail(email);

        if (user == null) {
            throw new UserNotFoundException("User not found");
        }

        return paymentRepository.findByUserId(user.getId());
    }

    public Payment paymentInfo(String uuid) {
        return paymentRepository.findByPaymentUuid(uuid).orElseThrow(() ->
                new PaymentNotFoundException("Payment not found"));
    }

    private void activateTariffForUser(User user, Tariff tariff, boolean autoRenewal, String paymentMethodId) {
        String type = tariff.getTariffAccountType();
        TariffInfo activeTariffInfo = user.getActiveTariffInfo();

        if (type.equals("token")) {
            Integer tokens = activeTariffInfo.getBoughtTokens();
            activeTariffInfo.setBoughtTokens(tokens + tariff.getTokens());

            tariffInfoRepository.save(activeTariffInfo);
        } else {
            TariffInfo tariffInfo = new TariffInfo();
            tariffInfo.setStatus(TariffStatus.ACTIVE);
            tariffInfo.setStartTariff(LocalDateTime.now());
            tariffInfo.setEndTariff(LocalDateTime.now().plusMonths(1));
            tariffInfo.setTokens(tariff.getTokens());
            tariffInfo.setBoughtTokens(activeTariffInfo.getBoughtTokens());
            tariffInfo.setTariff(tariff);
            activeTariffInfo.setAutoRenewal(autoRenewal);
            tariffInfo.setPaymentMethodId(paymentMethodId);

            tariffInfoService.linkUserTariffInfo(user, tariffInfoService.addTariffInfo(tariffInfo));
        }
    }
}
