"use client";

/**
 * Plan context — client-side source of truth for the active brand's
 * plan + entitlement checks. Wraps FeatureGate / useFeatureGate /
 * usePlan so every consumer reads from a single cached /api/me/plan
 * response (one fetch per brand change, not one per gate).
 *
 * Mount once in the app tree (done in AppShell, inside BrandProvider)
 * so the whole authenticated surface can call useFeatureGate() without
 * a fetch-per-component cascade.
 */

import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
import { useUser } from "@clerk/nextjs";
import { useBrand } from "@/context/brand-context";
import {
  PLANS, planHasFeature, recommendPlanFor, resolveEffectivePlan,
  type FeatureKey, type PlanId, type PlanLimits,
} from "@/lib/plan-config";

/** Read superadmin status from Clerk's session/user metadata. Mirrors
 *  the middleware admin gate's logic so client + server agree on who
 *  the platform owners are.
 *
 *  Returns null while Clerk is still loading (no flash of "locked"
 *  while the role resolves), true/false once the user is hydrated.
 */
function useIsSuperadmin(): boolean | null {
  const { user, isLoaded } = useUser();
  if (!isLoaded) return null;
  if (!user) return false;
  // useUser() exposes publicMetadata directly — the middleware reads
  // sessionClaims.metadata / publicMetadata, which Clerk projects from
  // the user's publicMetadata when the session-token template
  // includes it. Either path lands the same role string here.
  const meta = (user.publicMetadata ?? {}) as Record<string, unknown>;
  if (meta.role === "superadmin") return true;
  if (meta.isSuperadmin === true) return true;
  return false;
}

export interface PlanState {
  plan: PlanId;
  /** Trial collapses to growth once planExpiresAt has passed; this
   *  is the resolved plan the UI should treat as current. */
  effectivePlan: PlanId;
  billingCycle: "monthly" | "annual";
  planStartedAt: string | null;
  planExpiresAt: string | null;
  limits: PlanLimits;
  features: FeatureKey[] | ["*"];
  usage: {
    brandsUsed: number;
    aiCreditsUsed: number;
    aiCreditsResetAt: string | null;
  };
}

interface PlanContextValue {
  /** Null while the first fetch is in flight. */
  state: PlanState | null;
  loading: boolean;
  /** Re-fetch the plan (call after a successful upgrade). */
  refresh: () => Promise<void>;
  /** Modal control — call openUpgrade("rank_tracker") to pop the
   *  modal with the right pre-selected recommended plan. */
  openUpgrade: (trigger: UpgradeTrigger) => void;
  closeUpgrade: () => void;
  upgrade: UpgradeTrigger | null;
}

export type UpgradeTrigger =
  | { kind: "feature"; feature: FeatureKey }
  | { kind: "credit_limit"; creditKind: "ai" | "briefs" | "keywords" | "brands" }
  | { kind: "manual"; recommendedPlan?: PlanId };

const PlanContext = createContext<PlanContextValue | null>(null);

export function PlanProvider({ children }: { children: ReactNode }) {
  const { brand } = useBrand();
  const [state, setState] = useState<PlanState | null>(null);
  const [loading, setLoading] = useState(true);
  const [upgrade, setUpgrade] = useState<UpgradeTrigger | null>(null);

  const fetchPlan = useCallback(async () => {
    if (!brand.id) return;
    setLoading(true);
    try {
      const r = await fetch(`/api/me/plan?brandId=${brand.id}`);
      if (r.ok) setState(await r.json());
    } finally {
      setLoading(false);
    }
  }, [brand.id]);

  useEffect(() => { fetchPlan(); }, [fetchPlan]);

  const value: PlanContextValue = useMemo(() => ({
    state,
    loading,
    refresh: fetchPlan,
    openUpgrade: (trigger) => setUpgrade(trigger),
    closeUpgrade: () => setUpgrade(null),
    upgrade,
  }), [state, loading, fetchPlan, upgrade]);

  return <PlanContext.Provider value={value}>{children}</PlanContext.Provider>;
}

/** Low-level context consumer. Throws if called outside a
 *  PlanProvider — indicates a setup bug. */
export function usePlanContext(): PlanContextValue {
  const ctx = useContext(PlanContext);
  if (!ctx) throw new Error("usePlanContext must be used inside <PlanProvider>");
  return ctx;
}

/** Current-plan snapshot. Prefer this over pulling PLANS[x] manually
 *  so trial expiry is respected. */
export function usePlan(): { plan: PlanState | null; loading: boolean; refresh: () => Promise<void> } {
  const { state, loading, refresh } = usePlanContext();
  return { plan: state, loading, refresh };
}

/** Feature gate hook. Returns:
 *   canAccess — null while loading, true/false once the plan resolves
 *   showUpgrade() — opens the upgrade modal with this feature pre-
 *                   selected so the modal can recommend the right plan
 *   recommendedPlan — cheapest plan containing the feature (or
 *                     "enterprise" when only Enterprise has it)
 */
export interface FeatureGateResult {
  canAccess: boolean | null;
  showUpgrade: () => void;
  recommendedPlan: PlanId | null;
  currentPlan: PlanId | null;
  loading: boolean;
}

export function useFeatureGate(feature: FeatureKey): FeatureGateResult {
  const { state, loading, openUpgrade } = usePlanContext();
  // Superadmin bypass — platform owners never see the upgrade modal
  // or any locked overlays. Resolved before plan state so they don't
  // even need a brand to be selected for the bypass to fire.
  const isSuperadmin = useIsSuperadmin();

  return useMemo<FeatureGateResult>(() => {
    if (isSuperadmin) {
      return {
        canAccess: true,
        showUpgrade: () => { /* no-op for superadmins */ },
        recommendedPlan: null,
        currentPlan: state?.effectivePlan ?? null,
        loading: false,
      };
    }
    if (!state) {
      return {
        canAccess: null,
        showUpgrade: () => openUpgrade({ kind: "feature", feature }),
        recommendedPlan: recommendPlanFor(feature),
        currentPlan: null,
        loading,
      };
    }
    const canAccess = planHasFeature(
      { plan: state.plan, planExpiresAt: state.planExpiresAt ? new Date(state.planExpiresAt) : null },
      feature,
    );
    return {
      canAccess,
      showUpgrade: () => openUpgrade({ kind: "feature", feature }),
      recommendedPlan: recommendPlanFor(feature),
      currentPlan: state.effectivePlan,
      loading,
    };
  }, [state, loading, feature, openUpgrade, isSuperadmin]);
}

/** Resolve-only helper — does the current plan include this feature
 *  right now? Returns null while loading so callers can render a
 *  skeleton state without flashing "locked" then unlocking.
 *
 *  Superadmins always read true — clears every sidebar lock icon and
 *  any conditional UI gated on a feature key. */
export function useHasFeature(feature: FeatureKey): boolean | null {
  const { state } = usePlanContext();
  const isSuperadmin = useIsSuperadmin();
  if (isSuperadmin) return true;
  if (!state) return null;
  return planHasFeature(
    { plan: state.plan, planExpiresAt: state.planExpiresAt ? new Date(state.planExpiresAt) : null },
    feature,
  );
}

/** Readable feature label for the upgrade modal header. Falls back
 *  to the underscore-split key when the label isn't catalogued. */
export function featureLabel(feature: FeatureKey): string {
  const FEATURE_LABELS: Partial<Record<FeatureKey, string>> = {
    rank_tracker: "Rank Tracker",
    competitor_intel: "Competitor Intel",
    ai_visibility: "AI Visibility",
    backlinks: "Backlinks",
    industry_intel: "Industry Intelligence",
    schema_generator: "Schema Generator",
    report_studio: "Report Studio",
    seo_content_brief: "SEO Content Brief",
    llmo_content_brief: "LLMO Content Brief",
    writing_assistant: "Writing Assistant",
    page_studio: "Page Studio",
    client_portal: "Client Portal",
    custom_domain: "Custom Domain",
    email_reports: "Email Reports",
    client_management: "Client Management",
    conversion_intelligence: "Conversion Intelligence",
    topic_map: "Topic Map",
    priority_support: "Priority Support",
    api_access: "API Access",
    bulk_operations: "Bulk Operations",
    page_launch_tracker: "Page Launch Tracker",
    session_explorer: "Session Explorer",
    ux_frustration: "UX Frustration Signals",
    heatmaps: "Heatmaps",
    link_inspector: "Link Inspector",
    content_intel_full: "Full Content Intelligence",
    report_studio_full: "Full Report Studio",
    paid_media: "Paid Media Attribution",
    ott_attribution: "OTT/CTV Attribution",
    household_clustering: "Household Clustering",
    visitor_identity_graph: "Visitor Identity Graph",
    channel_classification: "Channel Classification",
  };
  return FEATURE_LABELS[feature] ?? feature.replace(/_/g, " ");
}

// Re-export PLANS so consumers don't need two imports.
export { PLANS, resolveEffectivePlan };
