import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { PLANS_CONFIG, SubscriptionPlanName } from './plans.config';

@Injectable()
export class SubscriptionEngineService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Validates a request against subscription status, expiry, permissions, and limits.
   * If a check fails, throws a ForbiddenException with standardized payload.
   */
  async validateRequest(businessId: string, feature?: string, limitToCheck?: string) {
    const business = await this.prisma.business.findUnique({
      where: { id: businessId },
    });

    if (!business) {
      throw new NotFoundException('Business not found');
    }

    // 1. Reset counters monthly if due
    await this.resetCountersIfDue(business);

    const now = new Date();

    // 2. Subscription Status & Expiry Checks
    if (business.subscriptionStatus === 'UNSUBSCRIBED') {
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Unsubscribed',
        requiredPlan: 'Starter',
        action: 'Choose Subscription or Start Trial',
      });
    }

    if (business.subscriptionStatus === 'EXPIRED') {
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Subscription Expired',
        requiredPlan: 'Starter',
        action: 'Upgrade Subscription',
      });
    }

    if (business.subscriptionStatus === 'CANCELLED') {
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Subscription Cancelled',
        requiredPlan: 'Starter',
        action: 'Upgrade Subscription',
      });
    }

    // Check Trial Expiry
    if (business.subscriptionStatus === 'TRIAL' && business.trialEnd && now > business.trialEnd) {
      await this.prisma.business.update({
        where: { id: businessId },
        data: { subscriptionStatus: 'EXPIRED' },
      });
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Trial Expired',
        requiredPlan: 'Starter',
        action: 'Upgrade Subscription',
      });
    }

    // Check Subscription Expiry
    if (business.subscriptionStatus === 'ACTIVE' && business.subscriptionEnd && now > business.subscriptionEnd) {
      await this.prisma.business.update({
        where: { id: businessId },
        data: { subscriptionStatus: 'EXPIRED' },
      });
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Subscription Expired',
        requiredPlan: 'Starter',
        action: 'Upgrade Subscription',
      });
    }

    const planName = business.subscriptionPlan as SubscriptionPlanName;
    const plan = PLANS_CONFIG[planName] || PLANS_CONFIG.FREE_TRIAL;

    // 3. Feature Permission Check
    if (feature && !plan.features.includes(feature)) {
      throw new ForbiddenException({
        status: 'DENIED',
        reason: 'Feature Locked',
        requiredPlan: this.getRequiredPlanForFeature(feature),
        action: 'Upgrade Subscription',
      });
    }

    // 4. Usage Limit Check
    if (limitToCheck) {
      const allowed = await this.verifyLimit(businessId, planName, limitToCheck);
      if (!allowed) {
        throw new ForbiddenException({
          status: 'DENIED',
          reason: 'Usage Limit Reached',
          requiredPlan: this.getRequiredPlanForLimit(limitToCheck),
          action: 'Upgrade Subscription',
        });
      }
    }

    return true;
  }

  /**
   * Soft validation (returns boolean instead of throwing)
   */
  async checkAccess(businessId: string, feature?: string, limitToCheck?: string): Promise<boolean> {
    try {
      await this.validateRequest(businessId, feature, limitToCheck);
      return true;
    } catch (err) {
      return false;
    }
  }

  /**
   * Increments transactional usage counters
   */
  async incrementCounter(businessId: string, counterName: 'deliveries' | 'api_requests') {
    const field = counterName === 'deliveries' ? 'deliveriesCount' : 'apiRequestsCount';
    await this.prisma.business.update({
      where: { id: businessId },
      data: {
        [field]: { increment: 1 },
      },
    });
  }

  private async verifyLimit(businessId: string, planName: SubscriptionPlanName, limitToCheck: string): Promise<boolean> {
    const plan = PLANS_CONFIG[planName] || PLANS_CONFIG.FREE_TRIAL;
    const limits = plan.limits;

    const business = await this.prisma.business.findUnique({
      where: { id: businessId },
      select: {
        deliveriesCount: true,
        apiRequestsCount: true,
      },
    });

    if (!business) return false;

    switch (limitToCheck) {
      case 'deliveries':
        return business.deliveriesCount < limits.deliveries;
      case 'api_requests':
        return business.apiRequestsCount < limits.api_requests;
      case 'products': {
        const count = await this.prisma.product.count({
          where: { businessId, status: 'ACTIVE' },
        });
        return count < limits.products;
      }
      case 'customers': {
        const count = await this.prisma.customer.count({
          where: { businessId },
        });
        return count < limits.customers;
      }
      case 'team_members':
        // Defaulting to 1 for now since we don't have separate sub-users.
        return 1 <= limits.team_members;
      default:
        return true;
    }
  }

  private async resetCountersIfDue(business: any) {
    const now = new Date();
    const lastReset = new Date(business.lastCounterReset);
    const thirtyDaysAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);

    if (lastReset < thirtyDaysAgo) {
      await this.prisma.business.update({
        where: { id: business.id },
        data: {
          deliveriesCount: 0,
          apiRequestsCount: 0,
          lastCounterReset: now,
        },
      });
    }
  }

  private getRequiredPlanForFeature(feature: string): string {
    for (const [planName, config] of Object.entries(PLANS_CONFIG)) {
      if (config.features.includes(feature)) {
        return config.name;
      }
    }
    return 'Professional';
  }

  private getRequiredPlanForLimit(limitName: string): string {
    if (limitName === 'deliveries') return 'Starter';
    if (limitName === 'customers') return 'Starter';
    if (limitName === 'products') return 'Starter';
    return 'Professional';
  }

  async confirmSubscription(reference: string, gatewayReference: string) {
    const payment = await this.prisma.subscriptionPayment.findUnique({
      where: { paymentReference: reference },
    });

    if (!payment || payment.status === 'SUCCESS') return;

    // Update payment status
    await this.prisma.subscriptionPayment.update({
      where: { id: payment.id },
      data: {
        status: 'SUCCESS',
        gatewayReference,
      },
    });

    const now = new Date();
    let subscriptionEnd = new Date();

    if (payment.billingCycle === 'monthly') {
      subscriptionEnd.setDate(now.getDate() + 30);
    } else if (payment.billingCycle === 'semi') {
      subscriptionEnd.setDate(now.getDate() + 180);
    } else if (payment.billingCycle === 'annual') {
      subscriptionEnd.setDate(now.getDate() + 365);
    }

    // Update business subscription details
    await this.prisma.business.update({
      where: { id: payment.businessId },
      data: {
        subscriptionStatus: 'ACTIVE',
        subscriptionPlan: payment.plan,
        onboardingStep: 'ONBOARDING_COMPLETED',
        subscriptionStart: now,
        subscriptionEnd,
        deliveriesCount: 0, // Reset counters
        apiRequestsCount: 0,
      },
    });
  }
}
