import { BillingService } from "./billing.server";
import { FeatureAccessDeniedError } from "../lib/security/errors";

export const PLAN_CAPABILITIES = {
  free: {
    productSearch: true,
    productDetails: true,
    cart: false,
    checkout: false,
    shippingRates: false,
    discounts: false,
    orderTracking: false,
    returnsAndCancellations: false,
    aiRecommendations: false,
    customerSupportAI: false,
    knowledgeBase: false,
    byok: false,
  },
  starter: {
    productSearch: true,
    productDetails: true,
    cart: true,
    checkout: true,
    shippingRates: true,
    discounts: true,
    orderTracking: false,
    returnsAndCancellations: false,
    aiRecommendations: false,
    customerSupportAI: false,
    knowledgeBase: false,
    byok: false,
  },
  pro: {
    productSearch: true,
    productDetails: true,
    cart: true,
    checkout: true,
    shippingRates: true,
    discounts: true,
    orderTracking: true,
    returnsAndCancellations: true,
    aiRecommendations: true,
    customerSupportAI: true,
    knowledgeBase: true,
    byok: true,
  },
  growth: {
    productSearch: true,
    productDetails: true,
    cart: true,
    checkout: true,
    shippingRates: true,
    discounts: true,
    orderTracking: true,
    returnsAndCancellations: true,
    aiRecommendations: true,
    customerSupportAI: true,
    knowledgeBase: true,
    byok: true,
  },
};

export type PlanFeatureKey = keyof typeof PLAN_CAPABILITIES.free;

export interface StorePlanCapabilities {
  shopId: string;
  planTier: "FREE" | "STARTER" | "PRO" | "GROWTH";
  features: typeof PLAN_CAPABILITIES.free;
}

export class PlanCapabilityService {
  /**
   * Authoritatively evaluates merchant plan capabilities strictly scoped to shopId.
   * Only active Shopify subscriptions (status === 'ACTIVE') grant paid capabilities.
   */
  static async getPlanCapabilities({
    shopId,
    shopifyDomain,
  }: {
    shopId: string;
    shopifyDomain?: string;
  }): Promise<StorePlanCapabilities> {
    const planInfo = await BillingService.getCurrentPlan(shopId);
    const isActive = planInfo?.status === "ACTIVE";
    const rawPlan = (planInfo?.plan || "FREE").toUpperCase() as "FREE" | "STARTER" | "PRO" | "GROWTH";
    const effectivePlan: "FREE" | "STARTER" | "PRO" | "GROWTH" = isActive ? rawPlan : "FREE";

    const normalizedKey = effectivePlan.toLowerCase() as keyof typeof PLAN_CAPABILITIES;
    const features = PLAN_CAPABILITIES[normalizedKey] || PLAN_CAPABILITIES.free;

    return {
      shopId: planInfo?.shopId || shopId,
      planTier: effectivePlan,
      features,
    };
  }

  /**
   * Authoritatively asserts that a plan allows a specific feature. Throws FeatureAccessDeniedError if denied.
   * Supports both:
   * 1. assertFeature({ shopId, feature })
   * 2. assertFeature(planTier, feature)
   */
  static async assertFeature(
    param1: string | { shopId: string; feature: PlanFeatureKey },
    param2?: PlanFeatureKey
  ): Promise<void> {
    if (typeof param1 === "object" && param1 !== null) {
      const { shopId, feature } = param1;
      const capabilities = await this.getPlanCapabilities({ shopId });
      if (!capabilities.features[feature]) {
        throw new FeatureAccessDeniedError(
          `Feature '${feature}' is not available on the ${capabilities.planTier} plan. Upgrade required via Shopify Billing.`
        );
      }
      return;
    }

    const planTier = param1;
    const feature = param2!;
    const normalizedKey = (planTier || "free").toLowerCase() as keyof typeof PLAN_CAPABILITIES;
    const features = PLAN_CAPABILITIES[normalizedKey] || PLAN_CAPABILITIES.free;
    if (!features[feature]) {
      throw new FeatureAccessDeniedError(
        `Feature '${feature}' is not available on the ${planTier.toUpperCase()} plan. Upgrade required via Shopify Billing.`
      );
    }
  }
}

/**
 * Authoritative feature assert helper.
 */
export async function assertFeature(
  param1: string | { shopId: string; feature: PlanFeatureKey },
  param2?: PlanFeatureKey
): Promise<void> {
  return PlanCapabilityService.assertFeature(param1 as any, param2 as any);
}

