/**
 * Package Assignment Service
 * Resolves a SubscriptionPackage's pricing items into concrete module, feature, and AI model slugs,
 * then snapshots them onto the CompanySubscription document.
 */

import { getModels } from '../models';
import { notificationService } from './notificationService';

export interface ResolvedPackageAccess {
  modules: string[];
  features: string[];
  aiModels: string[];
}

/**
 * Resolve a package's selectedPricingItemSlugs into categorized access lists.
 * Looks up each PricingItem to determine its categorySlug, then partitions:
 *   - categorySlug === 'modules' → modules
 *   - categorySlug === 'ai_features' → features
 *   - categorySlug === 'ai_models' → aiModels
 * Also merges the package's own features array (for boolean flags like api_access).
 */
export async function resolvePackageAccess(packageId: string): Promise<ResolvedPackageAccess> {
  const { SubscriptionPackage, PricingItem } = getModels();

  const pkg = await SubscriptionPackage.findById(packageId).lean();
  if (!pkg) {
    throw new Error(`Package not found: ${packageId}`);
  }

  // Combine selected pricing items and addons
  const allSlugs = [
    ...(pkg.selectedPricingItemSlugs || []),
    ...(pkg.selectedAddonSlugs || []),
  ];

  // Query PricingItems by slug
  const items = allSlugs.length > 0
    ? await PricingItem.find({ slug: { $in: allSlugs }, isActive: true }).lean()
    : [];

  const modules: string[] = [];
  const features: string[] = [];
  const aiModels: string[] = [];

  for (const item of items) {
    switch (item.categorySlug) {
      case 'modules':
        modules.push(item.slug);
        break;
      case 'ai_features':
        features.push(item.slug);
        break;
      case 'ai_models':
        aiModels.push(item.slug);
        break;
      case 'ai_providers':
        // When an AI provider is selected, include all its child models
        // (items where parentItemSlug matches this provider's slug)
        break;
      default:
        // usage_limits, languages, storage, etc. — skip for access lists
        break;
    }
  }

  // Also resolve ai_providers: if a provider slug is selected, include all its child models
  const providerSlugs = items
    .filter((item: any) => item.categorySlug === 'ai_providers')
    .map((item: any) => item.slug);

  if (providerSlugs.length > 0) {
    const childModels = await PricingItem.find({
      parentItemSlug: { $in: providerSlugs },
      categorySlug: 'ai_models',
      isActive: true,
    }).lean();

    for (const model of childModels) {
      if (!aiModels.includes(model.slug)) {
        aiModels.push(model.slug);
      }
    }
  }

  // Merge the package's own features array (boolean feature flags like 'api_access')
  if (pkg.features && Array.isArray(pkg.features)) {
    for (const feature of pkg.features) {
      if (!features.includes(feature)) {
        features.push(feature);
      }
    }
  }

  // Also merge boolean feature flags from the package
  if (pkg.apiAccess && !features.includes('api_access')) {
    features.push('api_access');
  }
  if (pkg.whiteLabel && !features.includes('white_label')) {
    features.push('white_label');
  }
  if (pkg.customBranding && !features.includes('custom_branding')) {
    features.push('custom_branding');
  }

  return { modules, features, aiModels };
}

/**
 * Assign a package's modules, features, and AI models to a company's subscription.
 * Creates the CompanySubscription if it doesn't exist, or updates it if it does.
 * Called on checkout, plan change, activation, and reactivation.
 */
export async function assignPackageToCompany(
  companyId: string,
  packageId: string,
  session?: any
): Promise<any> {
  const { CompanySubscription } = getModels();

  // Resolve the package into access lists
  const access = await resolvePackageAccess(packageId);

  // Find or create the company's subscription
  const existingSub = await CompanySubscription.findOne({ companyId });

  if (existingSub) {
    // Update existing subscription with the new access lists
    existingSub.modules = access.modules;
    existingSub.features = access.features;
    existingSub.aiModels = access.aiModels;
    await existingSub.save({ session });

    // A package change silently adds or removes modules from the sidebar, so
    // the org's admins are told rather than left to notice. Only the update
    // path notifies: the create path below is part of onboarding, where the
    // plan is what the admin just chose.
    void notificationService.notifyOrgRole(companyId, 'admin', {
      type: 'billing.package.updated',
      message: `Your plan was updated — ${access.modules.length} module(s) are now available.`,
      entityType: 'package',
      entityId: String(packageId),
      actionUrl: '/subscription',
      groupKey: `billing.package.updated:${companyId}:${packageId}`,
    });

    return existingSub;
  }

  // This shouldn't normally happen — the subscription should already exist
  // when this function is called. But handle it gracefully.
  console.warn(`[PackageAssignment] No subscription found for company ${companyId}. Creating one.`);
  const subscription = new CompanySubscription({
    companyId,
    packageId,
    status: 'active',
    billingCycle: 'monthly',
    startDate: new Date(),
    endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
    modules: access.modules,
    features: access.features,
    aiModels: access.aiModels,
  });
  await subscription.save({ session });
  return subscription;
}

/**
 * Get a company's current package access (modules, features, AI models).
 * Returns null if the company has no subscription.
 * Super-admin always gets full access (empty arrays = no restrictions).
 */
export async function getCompanyPackageAccess(companyId: string): Promise<ResolvedPackageAccess | null> {
  const { CompanySubscription } = getModels();

  const subscription = await CompanySubscription.findOne({ companyId }).lean();
  if (!subscription) {
    return null;
  }

  // If subscription is not active/trial/lifetime, they have no access
  if (!['active', 'trial'].includes(subscription.status) && !subscription.isLifetime) {
    return { modules: [], features: [], aiModels: [] };
  }

  // If the subscription has snapshotted access, use it
  if (subscription.modules && subscription.modules.length > 0) {
    return {
      modules: subscription.modules,
      features: subscription.features || [],
      aiModels: subscription.aiModels || [],
    };
  }

  // Fallback: resolve from package at runtime (slower, but handles legacy subscriptions)
  try {
    return await resolvePackageAccess(subscription.packageId);
  } catch (err) {
    console.error(`[PackageAssignment] Failed to resolve package access for company ${companyId}:`, err);
    return null;
  }
}