/**
 * Subscription Plans Service
 *
 * Service for managing subscription plans
 */

import { Repository, DataSource } from 'typeorm';
import { SubscriptionPlan, PlanType } from '../entities';
import { CreateSubscriptionPlanDto, UpdateSubscriptionPlanDto } from '../dto';

export class SubscriptionPlansService {
  private planRepository: Repository<SubscriptionPlan>;

  constructor(private dataSource: DataSource) {
    this.planRepository = dataSource.getRepository(SubscriptionPlan);
  }

  /**
   * Create a new subscription plan
   */
  async create(dto: CreateSubscriptionPlanDto): Promise<SubscriptionPlan> {
    // Check if code already exists
    const existing = await this.planRepository.findOne({
      where: { code: dto.code },
    });

    if (existing) {
      throw new Error(`Plan with code ${dto.code} already exists`);
    }

    const plan = this.planRepository.create({
      code: dto.code,
      name: dto.name,
      description: dto.description,
      planType: dto.planType || 'saas',
      baseMonthlyPrice: dto.baseMonthlyPrice,
      baseAnnualPrice: dto.baseAnnualPrice,
      setupFee: dto.setupFee || 0,
      maxUsers: dto.maxUsers || 5,
      maxBranches: dto.maxBranches || 1,
      storageGb: dto.storageGb || 10,
      apiCallsMonthly: dto.apiCallsMonthly || 10000,
      includedModules: dto.includedModules || [],
      includedPlatforms: dto.includedPlatforms || ['web'],
      features: dto.features || {},
      isActive: dto.isActive !== false,
      isPublic: dto.isPublic !== false,
    });

    return this.planRepository.save(plan);
  }

  /**
   * Find all plans
   */
  async findAll(options?: {
    isActive?: boolean;
    isPublic?: boolean;
    planType?: PlanType;
  }): Promise<SubscriptionPlan[]> {
    const query = this.planRepository.createQueryBuilder('plan');

    if (options?.isActive !== undefined) {
      query.andWhere('plan.isActive = :isActive', { isActive: options.isActive });
    }

    if (options?.isPublic !== undefined) {
      query.andWhere('plan.isPublic = :isPublic', { isPublic: options.isPublic });
    }

    if (options?.planType) {
      query.andWhere('plan.planType = :planType', { planType: options.planType });
    }

    return query.orderBy('plan.baseMonthlyPrice', 'ASC').getMany();
  }

  /**
   * Find public plans (for pricing page)
   */
  async findPublicPlans(): Promise<SubscriptionPlan[]> {
    return this.findAll({ isActive: true, isPublic: true });
  }

  /**
   * Find plan by ID
   */
  async findById(id: string): Promise<SubscriptionPlan | null> {
    return this.planRepository.findOne({ where: { id } });
  }

  /**
   * Find plan by code
   */
  async findByCode(code: string): Promise<SubscriptionPlan | null> {
    return this.planRepository.findOne({ where: { code } });
  }

  /**
   * Update a plan
   */
  async update(id: string, dto: UpdateSubscriptionPlanDto): Promise<SubscriptionPlan> {
    const plan = await this.findById(id);
    if (!plan) {
      throw new Error('Plan not found');
    }

    Object.assign(plan, dto);
    return this.planRepository.save(plan);
  }

  /**
   * Soft delete a plan
   */
  async delete(id: string): Promise<void> {
    const plan = await this.findById(id);
    if (!plan) {
      throw new Error('Plan not found');
    }

    // Check if plan has active subscriptions
    const subscriptionCount = await this.dataSource
      .createQueryBuilder()
      .select('COUNT(*)')
      .from('billing.tenant_subscriptions', 'ts')
      .where('ts.plan_id = :planId', { planId: id })
      .andWhere("ts.status IN ('active', 'trial')")
      .getRawOne();

    if (parseInt(subscriptionCount.count) > 0) {
      throw new Error('Cannot delete plan with active subscriptions');
    }

    await this.planRepository.softDelete(id);
  }

  /**
   * Activate/deactivate a plan
   */
  async setActive(id: string, isActive: boolean): Promise<SubscriptionPlan> {
    return this.update(id, { isActive });
  }

  /**
   * Compare two plans
   */
  async comparePlans(
    planId1: string,
    planId2: string
  ): Promise<{
    plan1: SubscriptionPlan;
    plan2: SubscriptionPlan;
    differences: Record<string, { plan1: any; plan2: any }>;
  }> {
    const [plan1, plan2] = await Promise.all([
      this.findById(planId1),
      this.findById(planId2),
    ]);

    if (!plan1 || !plan2) {
      throw new Error('One or both plans not found');
    }

    const fieldsToCompare = [
      'baseMonthlyPrice',
      'baseAnnualPrice',
      'maxUsers',
      'maxBranches',
      'storageGb',
      'apiCallsMonthly',
    ];

    const differences: Record<string, { plan1: any; plan2: any }> = {};

    for (const field of fieldsToCompare) {
      if ((plan1 as any)[field] !== (plan2 as any)[field]) {
        differences[field] = {
          plan1: (plan1 as any)[field],
          plan2: (plan2 as any)[field],
        };
      }
    }

    // Compare included modules
    const modules1 = new Set(plan1.includedModules);
    const modules2 = new Set(plan2.includedModules);
    const modulesDiff = {
      onlyInPlan1: plan1.includedModules.filter((m) => !modules2.has(m)),
      onlyInPlan2: plan2.includedModules.filter((m) => !modules1.has(m)),
    };
    if (modulesDiff.onlyInPlan1.length > 0 || modulesDiff.onlyInPlan2.length > 0) {
      differences.includedModules = {
        plan1: modulesDiff.onlyInPlan1,
        plan2: modulesDiff.onlyInPlan2,
      };
    }

    return { plan1, plan2, differences };
  }
}
