package com.saas.subscription.service;

import com.saas.subscription.entity.SubscriptionPlan;
import com.saas.subscription.entity.SubscriptionStatus;
import com.saas.subscription.entity.TenantSubscription;
import com.saas.subscription.repository.SubscriptionPlanRepository;
import com.saas.subscription.repository.SubscriptionPlanRepository;
import com.saas.subscription.repository.SubscriptionRepository;
import com.saas.subscription.repository.PaymentRecordRepository;
import com.saas.subscription.entity.PaymentRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class SubscriptionService {

    private final SubscriptionRepository subscriptionRepository;
    private final SubscriptionPlanRepository planRepository;
    private final PaymentRecordRepository paymentRecordRepository;

    // --- Admin: Plan Management ---

    @Transactional
    public SubscriptionPlan createPlan(SubscriptionPlan plan) {
        log.info("Creating new subscription plan: {}", plan.getName());
        return planRepository.save(plan);
    }

    public List<SubscriptionPlan> getAllPlans() {
        return planRepository.findAll();
    }

    public Optional<SubscriptionPlan> getPlan(Long id) {
        return planRepository.findById(id);
    }

    public List<SubscriptionPlan> getPublicPlans() {
        return planRepository.findAll().stream().filter(SubscriptionPlan::isActive).toList();
    }

    // --- Payments ---

    public List<PaymentRecord> getAllPayments() {
        return paymentRecordRepository.findAllByOrderByCreatedAtDesc();
    }

    public List<PaymentRecord> getTenantPayments(String tenantId) {
        return paymentRecordRepository.findByTenantIdOrderByCreatedAtDesc(tenantId);
    }

    // --- Tenant: Subscription Management ---

    @Transactional
    public TenantSubscription assignPlanToTenant(String tenantId, Long planId) {
        log.info("Assigning plan {} to tenant {}", planId, tenantId);

        SubscriptionPlan plan = planRepository.findById(planId)
                .orElseThrow(() -> new RuntimeException("Plan not found with ID: " + planId));

        TenantSubscription subscription = subscriptionRepository.findByTenantId(tenantId)
                .orElse(TenantSubscription.builder().tenantId(tenantId).createdAt(LocalDateTime.now()).build());

        subscription.setPlan(plan);
        subscription.setStatus(SubscriptionStatus.ACTIVE);

        // Reset period if it's a new subscription or upgrade
        // In a real system, we might handle upgrades differently (prorating)
        subscription.setCurrentPeriodStart(LocalDateTime.now());
        subscription.setCurrentPeriodEnd(LocalDateTime.now().plusMonths(1));
        subscription.setAutoRenew(true);

        // Reset usage counters
        subscription.setUsedMinutes(0);
        subscription.setUsedRecordingRetrievals(0);
        subscription.setOverageMinutes(0);

        return subscriptionRepository.save(subscription);
    }

    @Transactional
    public TenantSubscription assignDefaultPlan(String tenantId, String stripeCustomerId) {
        // Find a default plan (e.g., the one with lowest price or specific name)
        // For now, just pick the first one or throw if none
        SubscriptionPlan defaultPlan = planRepository.findAll().stream()
                .filter(p -> p.getPricePerMonth().doubleValue() == 0).findFirst()
                .orElseGet(() -> planRepository.findAll().stream().findFirst()
                        .orElseThrow(() -> new RuntimeException("No subscription plans available")));

        TenantSubscription sub = assignPlanToTenant(tenantId, defaultPlan.getId());
        sub.setStripeCustomerId(stripeCustomerId);
        return subscriptionRepository.save(sub);
    }

    public Optional<TenantSubscription> getTenantSubscription(String tenantId) {
        return subscriptionRepository.findByTenantId(tenantId);
    }

    // --- Limit Checking (Hard Limits) ---

    public boolean canCreateUser(String tenantId, long currentUserCount) {
        return subscriptionRepository.findByTenantId(tenantId).map(sub -> {
            if (sub.getStatus() != SubscriptionStatus.ACTIVE)
                return false;
            return currentUserCount < sub.getPlan().getMaxUsers();
        }).orElse(false); // Default
                          // to
                          // false
                          // if
                          // no
                          // subscription
    }

    public boolean canCreateAiAgent(String tenantId, long currentAgentCount) {
        return subscriptionRepository.findByTenantId(tenantId).map(sub -> {
            if (sub.getStatus() != SubscriptionStatus.ACTIVE)
                return false;
            return currentAgentCount < sub.getPlan().getMaxActiveAiAgents();
        }).orElse(false);
    }

    // --- Usage Tracking (Soft Limits) ---

    /**
     * Tracks call usage and determines if the call is overage.
     * 
     * @param tenantId        The tenant ID
     * @param durationMinutes The duration of the call in minutes
     * @return true if the call (or part of it) is overage
     */
    @Transactional
    public boolean trackCallUsage(String tenantId, int durationMinutes) {
        Optional<TenantSubscription> subOpt = subscriptionRepository.findByTenantId(tenantId);

        if (subOpt.isEmpty()) {
            log.warn("Usage tracking: No subscription found for tenant {}", tenantId);
            return true; // Treat as
                         // overage/unauthorized
        }

        TenantSubscription sub = subOpt.get();

        // If subscription is not active, everything is overage/invalid
        if (sub.getStatus() != SubscriptionStatus.ACTIVE) {
            return true;
        }

        int included = sub.getPlan().getIncludedMinutes();
        int usedBefore = sub.getUsedMinutes();
        int usedAfter = usedBefore + durationMinutes;

        boolean isOverage = false;

        // Check if we are crossing the limit or already above it
        if (usedAfter > included) {
            isOverage = true;

            // Calculate how many of THESE minutes are overage
            int overageForThisCall;
            if (usedBefore >= included) {
                // Already over limit, entire call is overage
                overageForThisCall = durationMinutes;
            } else {
                // Crossed the limit during this call
                overageForThisCall = usedAfter - included;
            }

            sub.setOverageMinutes(sub.getOverageMinutes() + overageForThisCall);
            log.info("⚠️ Overage detected for tenant {}: {} minutes added (Total overage: {})", tenantId,
                    overageForThisCall, sub.getOverageMinutes());
        }

        sub.setUsedMinutes(usedAfter);
        subscriptionRepository.save(sub);

        return isOverage;
    }
}
