/**
 * Subscription Plan Service
 * Manages subscription plans for SaaS billing
 *
 * @module BillingUsage
 */

import { Repository, FindOptionsWhere, ILike } from 'typeorm';
import { AppDataSource } from '../../../shared/database/typeorm.config';
import { SubscriptionPlan, PlanType } from '../entities/subscription-plan.entity';
import { PlanFeature } from '../entities/plan-feature.entity';
import { PlanLimit } from '../entities/plan-limit.entity';

export interface CreateSubscriptionPlanDto {
  code: string;
  name: string;
  description?: string;
  planType?: PlanType;
  baseMonthlyPrice: number;
  baseAnnualPrice?: number;
  setupFee?: number;
  maxUsers?: number;
  maxBranches?: number;
  storageGb?: number;
  apiCallsMonthly?: number;
  includedModules?: string[];
  includedPlatforms?: string[];
  features?: Record<string, boolean>;
  isActive?: boolean;
  isPublic?: boolean;
}

export interface UpdateSubscriptionPlanDto {
  name?: string;
  description?: string;
  planType?: PlanType;
  baseMonthlyPrice?: number;
  baseAnnualPrice?: number;
  setupFee?: number;
  maxUsers?: number;
  maxBranches?: number;
  storageGb?: number;
  apiCallsMonthly?: number;
  includedModules?: string[];
  includedPlatforms?: string[];
  features?: Record<string, boolean>;
  isActive?: boolean;
  isPublic?: boolean;
}

export interface SubscriptionPlanFilters {
  planType?: PlanType;
  isActive?: boolean;
  isPublic?: boolean;
  search?: string;
}

export interface CreatePlanFeatureDto {
  planId: string;
  featureKey: string;
  featureName: string;
  category?: string;
  enabled?: boolean;
  configuration?: Record<string, any>;
  description?: string;
}

export interface CreatePlanLimitDto {
  planId: string;
  limitKey: string;
  limitName: string;
  limitValue: number;
  limitType?: 'monthly' | 'daily' | 'total' | 'per_user';
  allowOverage?: boolean;
  overageUnitPrice?: number;
  overageCurrency?: string;
}

export class SubscriptionPlanService {
  private planRepository: Repository<SubscriptionPlan>;
  private featureRepository: Repository<PlanFeature>;
  private limitRepository: Repository<PlanLimit>;

  constructor() {
    this.planRepository = AppDataSource.getRepository(SubscriptionPlan);
    this.featureRepository = AppDataSource.getRepository(PlanFeature);
    this.limitRepository = AppDataSource.getRepository(PlanLimit);
  }

  async findAll(filters: SubscriptionPlanFilters = {}): Promise<SubscriptionPlan[]> {
    const where: FindOptionsWhere<SubscriptionPlan> = {};

    if (filters.planType) {
      where.planType = filters.planType;
    }

    if (filters.isActive !== undefined) {
      where.isActive = filters.isActive;
    }

    if (filters.isPublic !== undefined) {
      where.isPublic = filters.isPublic;
    }

    if (filters.search) {
      where.name = ILike(`%${filters.search}%`);
    }

    return this.planRepository.find({
      where,
      order: { baseMonthlyPrice: 'ASC' },
    });
  }

  async findById(id: string): Promise<SubscriptionPlan | null> {
    return this.planRepository.findOne({
      where: { id },
    });
  }

  async findByCode(code: string): Promise<SubscriptionPlan | null> {
    return this.planRepository.findOne({
      where: { code },
    });
  }

  async findPublicPlans(): Promise<SubscriptionPlan[]> {
    return this.planRepository.find({
      where: { isActive: true, isPublic: true },
      order: { baseMonthlyPrice: 'ASC' },
    });
  }

  async create(data: CreateSubscriptionPlanDto): Promise<SubscriptionPlan> {
    const plan = this.planRepository.create(data);
    return this.planRepository.save(plan);
  }

  async update(id: string, data: UpdateSubscriptionPlanDto): Promise<SubscriptionPlan | null> {
    const plan = await this.findById(id);
    if (!plan) {
      return null;
    }

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

  async delete(id: string): Promise<boolean> {
    const result = await this.planRepository.softDelete(id);
    return result.affected ? result.affected > 0 : false;
  }

  // Plan Features
  async getPlanFeatures(planId: string): Promise<PlanFeature[]> {
    return this.featureRepository.find({
      where: { planId },
      order: { category: 'ASC', featureName: 'ASC' },
    });
  }

  async addPlanFeature(data: CreatePlanFeatureDto): Promise<PlanFeature> {
    const feature = this.featureRepository.create(data);
    return this.featureRepository.save(feature);
  }

  async updatePlanFeature(
    id: string,
    data: Partial<CreatePlanFeatureDto>
  ): Promise<PlanFeature | null> {
    const feature = await this.featureRepository.findOne({ where: { id } });
    if (!feature) {
      return null;
    }

    Object.assign(feature, data);
    return this.featureRepository.save(feature);
  }

  async deletePlanFeature(id: string): Promise<boolean> {
    const result = await this.featureRepository.delete(id);
    return result.affected ? result.affected > 0 : false;
  }

  // Plan Limits
  async getPlanLimits(planId: string): Promise<PlanLimit[]> {
    return this.limitRepository.find({
      where: { planId },
      order: { limitName: 'ASC' },
    });
  }

  async addPlanLimit(data: CreatePlanLimitDto): Promise<PlanLimit> {
    const limit = this.limitRepository.create(data);
    return this.limitRepository.save(limit);
  }

  async updatePlanLimit(id: string, data: Partial<CreatePlanLimitDto>): Promise<PlanLimit | null> {
    const limit = await this.limitRepository.findOne({ where: { id } });
    if (!limit) {
      return null;
    }

    Object.assign(limit, data);
    return this.limitRepository.save(limit);
  }

  async deletePlanLimit(id: string): Promise<boolean> {
    const result = await this.limitRepository.delete(id);
    return result.affected ? result.affected > 0 : false;
  }

  async getPlanWithDetails(id: string): Promise<{
    plan: SubscriptionPlan;
    features: PlanFeature[];
    limits: PlanLimit[];
  } | null> {
    const plan = await this.findById(id);
    if (!plan) {
      return null;
    }

    const [features, limits] = await Promise.all([
      this.getPlanFeatures(id),
      this.getPlanLimits(id),
    ]);

    return { plan, features, limits };
  }

  async comparePlans(planIds: string[]): Promise<{
    plans: SubscriptionPlan[];
    features: Record<string, PlanFeature[]>;
    limits: Record<string, PlanLimit[]>;
  }> {
    const plans = await this.planRepository.find({
      where: planIds.map((id) => ({ id })),
      order: { baseMonthlyPrice: 'ASC' },
    });

    const features: Record<string, PlanFeature[]> = {};
    const limits: Record<string, PlanLimit[]> = {};

    for (const plan of plans) {
      features[plan.id] = await this.getPlanFeatures(plan.id);
      limits[plan.id] = await this.getPlanLimits(plan.id);
    }

    return { plans, features, limits };
  }
}
