"use client";

/**
 * Brand Context — single source of truth for active site context.
 *
 * Provides to every page/component:
 *   - orgId          — active organization
 *   - brand / brands — active brand + list
 *   - siteId         — active TrackedSite (or null if none)
 *   - domain         — shortcut to brand.domain
 *   - integrations   — connected source statuses (ga4, gsc, gbp, etc.)
 *
 * On mount:
 *   1. Calls GET /api/me to get the user's org
 *   2. Calls GET /api/brands?orgId=xxx to get real brands
 *   3. Sets the first brand as active
 *
 * On brand change:
 *   4. Fetches site tag status for the active brand
 *   5. Fetches integration statuses for the active brand
 */

import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react";

export interface Brand {
  id:       string;
  name:     string;
  domain:   string;
  initials: string;
  color:    string;
  logoUrl:  string | null;
  isDemo:   boolean;
}

export interface IntegrationStatus {
  integrationId: string;
  connected: boolean;
  status: string;
  lastSynced: string | null;
}

export interface SourceStatusEntry {
  id: string;
  label: string;
  status: "connected" | "not_connected" | "no_data" | "queued" | "running" | "completed" | "stale" | "failed" | "partial_failure" | "partially_available" | "awaiting_first_run";
  lastUpdated: string | null;
  detail: string | null;
}

interface BrandContextValue {
  /** Active organization ID */
  orgId:          string;
  /** Active brand */
  brand:          Brand;
  /** All brands in the org */
  brands:         Brand[];
  /** Set the active brand */
  setBrand:       (brand: Brand) => void;
  /** Active TrackedSite ID (null if no site tag installed) */
  siteId:         string | null;
  /** True when the active brand has the Site Tag installed and collecting
   *  real data (installedAt non-null). Single source of truth for install
   *  status across all surfaces. */
  siteTagInstalled: boolean;
  /** The brand ID whose /api/site-tag fetch has fully resolved. Used to gate
   *  UI elements that depend on siteTagInstalled so they do not render while
   *  the status is still in-flight (covers both initial load and brand switches). */
  siteTagResolvedForBrandId: string;
  /** Shortcut: active brand's domain */
  domain:         string;
  /** Connected integration statuses for the active brand */
  integrations:   IntegrationStatus[];
  /** Whether a specific integration is connected */
  isConnected:    (integrationId: string) => boolean;
  /** Real status of every platform data source */
  sourceStatuses: Record<string, SourceStatusEntry>;
  /** Get the status of a specific source by ID */
  getSourceStatus: (sourceId: string) => SourceStatusEntry | null;
  /** Initial load in progress */
  loading:        boolean;
  /** True if brand fetching failed (network error, 403, etc.) */
  fetchError:     boolean;
  /** Reload brands, site, and integrations */
  refresh:        () => Promise<void>;
}

/** Fallback brand shown while loading */
const PLACEHOLDER_BRAND: Brand = {
  id: "", name: "Loading...", domain: "", initials: "...", color: "bg-slate-400", logoUrl: null, isDemo: false,
};

const BrandContext = createContext<BrandContextValue>({
  orgId:          "",
  brand:          PLACEHOLDER_BRAND,
  brands:         [],
  setBrand:       () => {},
  siteId:         null,
  siteTagInstalled: false,
  siteTagResolvedForBrandId: "",
  domain:         "",
  integrations:   [],
  isConnected:    () => false,
  sourceStatuses: {},
  getSourceStatus: () => null,
  loading:        true,
  fetchError:     false,
  refresh:        async () => {},
});

/** Color palette for brands that don't have a saved color */
const COLORS = ["bg-brand-500", "bg-emerald-500", "bg-violet-500", "bg-orange-500", "bg-sky-500", "bg-rose-500"];

const STORAGE_KEY = "meseo_selected_brand_id";

export function BrandProvider({ children }: { children: ReactNode }) {
  const [orgId, setOrgId] = useState("");
  const [brands, setBrands] = useState<Brand[]>([]);
  const [brand, setBrandState] = useState<Brand>(PLACEHOLDER_BRAND);
  const [siteId, setSiteId] = useState<string | null>(null);
  const [siteTagInstalled, setSiteTagInstalled] = useState(false);
  // Tracks which brand ID has had its /api/site-tag response resolved.
  // "" means no brand is resolved yet (initial load or mid-switch).
  // Gates siteTagInstalled-dependent UI so it only renders when the
  // current brand's status is known -- covers both initial load and switches.
  const [siteTagResolvedForBrandId, setSiteTagResolvedForBrandId] = useState("");
  const [integrations, setIntegrations] = useState<IntegrationStatus[]>([]);
  const [sourceStatuses, setSourceStatuses] = useState<Record<string, SourceStatusEntry>>({});
  const [loading, setLoading] = useState(true);

  // Check if a specific integration is connected
  const isConnected = useCallback(
    (integrationId: string) => integrations.some((i) => i.integrationId === integrationId && i.connected),
    [integrations]
  );

  // Get the status of a specific source
  const getSourceStatus = useCallback(
    (sourceId: string): SourceStatusEntry | null => sourceStatuses[sourceId] ?? null,
    [sourceStatuses]
  );

  // Wrap setBrand to also persist to localStorage
  const setBrand = useCallback((b: Brand) => {
    setBrandState(b);
    try { localStorage.setItem(STORAGE_KEY, b.id); } catch { /* ignore */ }
  }, []);

  // Load site + integration + source statuses for a brand
  const loadBrandContext = useCallback(async (brandId: string) => {
    if (!brandId) return;

    // Fetch site tag, integrations, and source statuses in parallel
    const [siteRes, intRes, srcRes] = await Promise.all([
      fetch(`/api/site-tag?brandId=${brandId}`, { cache: "no-store" }).catch(() => null),
      fetch(`/api/integrations?brandId=${brandId}`, { cache: "no-store" }).catch(() => null),
      fetch(`/api/source-status?brandId=${brandId}`, { cache: "no-store" }).catch(() => null),
    ]);

    // Site tag — /api/site-tag returns { site: TrackedSite | null, ... }.
    // The siteId we want is the public-facing siteId on TrackedSite
    // (e.g. "ms_clxyz...") that the audit overlay reads from the
    // meseo_tag_id query param. Previous read used `siteData.id` /
    // `siteData.siteId` (top-level fields that don't exist on this
    // response), so the brand-context's siteId was always null even
    // when the tag was installed and producing data — the Technical
    // Audit page's "Install the meSEO Site Tag" banner showed
    // permanently because of this read bug.
    if (siteRes?.ok) {
      const siteData = await siteRes.json().catch(() => null);
      const resolved = siteData?.site?.siteId ?? siteData?.site?.id ?? null;
      console.info("[brand-context] site-tag resolve", { brandId, hasSiteData: !!siteData, hasSite: !!siteData?.site, resolved });
      setSiteId(resolved);
      // installedAt is non-null when the tag has captured real events (set by
      // the maturity-based logic in getSiteTagMaturity / getSiteTagInstalledAt).
      // This is the single source of truth for "is the tag installed."
      setSiteTagInstalled(!!(siteData?.installedAt));
    } else {
      setSiteId(null);
      setSiteTagInstalled(false);
    }
    // Mark this brand's site-tag status as resolved. Set after both the
    // success and failure paths above so the resolved ID always reflects
    // the brand whose final status is now in siteTagInstalled.
    setSiteTagResolvedForBrandId(brandId);

    // Integrations
    if (intRes?.ok) {
      const intData = await intRes.json().catch(() => []);
      if (Array.isArray(intData)) {
        setIntegrations(intData.map((i: Record<string, unknown>) => ({
          integrationId: i.integrationId as string,
          connected: i.connected as boolean,
          status: (i.status as string) ?? "idle",
          lastSynced: (i.lastSynced as string) ?? null,
        })));
      }
    } else {
      setIntegrations([]);
    }

    // Source statuses
    if (srcRes?.ok) {
      const srcData = await srcRes.json().catch(() => ({}));
      setSourceStatuses(srcData ?? {});
    } else {
      setSourceStatuses({});
    }
  }, []);

  const [fetchError, setFetchError] = useState(false);

  const loadBrands = useCallback(async () => {
    try {
      const meRes = await fetch("/api/me");
      if (!meRes.ok) {
        console.error("[brand-context] /api/me failed:", meRes.status);
        setFetchError(true);
        setLoading(false);
        return;
      }
      const meData = await meRes.json();

      // Try organizations from /api/me first; if empty, try fetching
      // all orgs the user might belong to as a fallback. This catches
      // cases where getCurrentUser() creates a new User record (no
      // memberships yet) but /api/me auto-creates an org.
      let resolvedOrgId = meData?.organizations?.[0]?.id;
      if (!resolvedOrgId) {
        console.warn("[brand-context] No orgId from /api/me, retrying...", { userEmail: meData?.user?.email });
        // Second attempt — /api/me should have auto-created an org
        const retryRes = await fetch("/api/me");
        if (retryRes.ok) {
          const retryData = await retryRes.json();
          resolvedOrgId = retryData?.organizations?.[0]?.id;
        }
        if (!resolvedOrgId) {
          console.error("[brand-context] Still no orgId after retry");
          setLoading(false);
          return;
        }
      }
      setOrgId(resolvedOrgId);

      const res = await fetch(`/api/brands?orgId=${resolvedOrgId}`, { cache: "no-store" });
      if (!res.ok) {
        console.error("[brand-context] /api/brands failed:", res.status, "orgId:", resolvedOrgId);
        // If 403 or 400, the orgId might be wrong. Try all orgs.
        if (res.status === 403 || res.status === 400) {
          const allOrgs = meData?.organizations ?? [];
          for (const org of allOrgs) {
            if (org.id === resolvedOrgId) continue;
            const fallbackRes = await fetch(`/api/brands?orgId=${org.id}`, { cache: "no-store" });
            if (fallbackRes.ok) {
              const fallbackData = await fallbackRes.json();
              if (Array.isArray(fallbackData) && fallbackData.length > 0) {
                resolvedOrgId = org.id;
                setOrgId(resolvedOrgId);
                break;
              }
            }
          }
        }
        setFetchError(true);
        setLoading(false);
        return;
      }
      const data = await res.json();

      if (Array.isArray(data) && data.length > 0) {
        const mapped: Brand[] = data.map((b: Record<string, unknown>, i: number) => ({
          id: b.id as string,
          name: b.name as string,
          domain: b.domain as string,
          initials: (b.initials as string) ?? (b.name as string).slice(0, 2).toUpperCase(),
          color: (b.color as string) ?? COLORS[i % COLORS.length],
          logoUrl: ((b.profile as Record<string, unknown> | undefined)?.logoUrl as string) ?? null,
          isDemo: (b.isDemo as boolean) ?? false,
        }));
        setBrands(mapped);

        // Restore saved selection from localStorage, or keep current, or pick first
        let savedId: string | null = null;
        try { savedId = localStorage.getItem(STORAGE_KEY); } catch { /* ignore */ }

        let selected: Brand;
        const saved = savedId ? mapped.find((b) => b.id === savedId) : null;
        if (saved) {
          selected = saved;
        } else {
          selected = mapped[0];
        }
        setBrandState(selected);

        // Load site + integrations for the selected brand
        await loadBrandContext(selected.id);
      } else {
        setBrands([]);
        setBrandState(PLACEHOLDER_BRAND);
      }
    } catch {
      // Silently fail — app still works with placeholder
    }
    setLoading(false);
  }, [loadBrandContext]);

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

  // Reload site + integrations when brand changes
  useEffect(() => {
    if (brand.id && brand.id !== PLACEHOLDER_BRAND.id) {
      loadBrandContext(brand.id);
    }
  }, [brand.id, loadBrandContext]);

  const refresh = useCallback(async () => {
    await loadBrands();
  }, [loadBrands]);

  return (
    <BrandContext.Provider value={{
      orgId,
      brand,
      brands,
      setBrand,
      siteId,
      siteTagInstalled,
      siteTagResolvedForBrandId,
      domain: brand.domain,
      integrations,
      isConnected,
      sourceStatuses,
      getSourceStatus,
      loading,
      fetchError,
      refresh,
    }}>
      {children}
    </BrandContext.Provider>
  );
}

export function useBrand() {
  return useContext(BrandContext);
}
