'use client';

import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import type { Session, User } from '@supabase/supabase-js';
import { getBrowserSupabase } from '@/lib/supabase';
import { readRefCookie } from '@/lib/partnerRef';

export type Tier = 'free' | 'analyst' | 'desk';

interface UserProfile {
  tier: Tier;
  scan_count: number;
  scan_reset_date: string;
  founder_pricing: boolean;
  subscription_status: 'none' | 'active' | 'grace_period' | 'expired' | 'canceled';
  subscription_end: string | null;
  mfa_setup_prompted: boolean;
}

interface AuthContextValue {
  user: User | null;
  session: Session | null;
  profile: UserProfile | null;
  tier: Tier;
  scanCount: number;
  scansRemaining: number | null;
  canScan: boolean;
  loading: boolean;
  signUp: (email: string, password: string, ageVerified: boolean) => Promise<{ error?: string }>;
  signIn: (email: string, password: string) => Promise<{ error?: string }>;
  signInWithGoogle: () => Promise<void>;
  // Session 13 — generalized OAuth dispatch. Apple/Twitter call paths
  // exist in the UI; whether the call SUCCEEDS depends on the
  // provider being configured in the Supabase dashboard. Unconfigured
  // providers return an error string the login page surfaces inline.
  signInWithProvider: (provider: 'google' | 'apple' | 'twitter') => Promise<{ error?: string }>;
  signOut: () => Promise<void>;
  refresh: () => Promise<void>;
  bumpScanCount: () => void;
  // Marks the MFA setup nag as seen so we don't ask the same user again.
  // Independent of whether they actually enabled MFA.
  markMFAPrompted: () => Promise<void>;
}

const FREE_LIMIT = 5; // reads per calendar month
const monthKey = () => new Date().toISOString().slice(0, 7) + '-01'; // YYYY-MM-01
const isSameMonth = (date: string | null | undefined) =>
  !!date && date.slice(0, 7) === new Date().toISOString().slice(0, 7);

const AuthContext = createContext<AuthContextValue | null>(null);

export default function AuthProvider({ children }: { children: React.ReactNode }) {
  const supabase = getBrowserSupabase();
  const [user, setUser] = useState<User | null>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [profile, setProfile] = useState<UserProfile | null>(null);
  const [loading, setLoading] = useState(true);

  const loadProfile = useCallback(
    async (currentUser: User | null) => {
      if (!supabase || !currentUser) {
        setProfile(null);
        return;
      }
      const { data, error } = await supabase
        .from('user_profiles')
        .select('tier, scan_count, scan_reset_date, founder_pricing, subscription_status, subscription_end, mfa_setup_prompted')
        .eq('id', currentUser.id)
        .single();

      if (error || !data) {
        setProfile({
          tier: 'free',
          scan_count: 0,
          scan_reset_date: monthKey(),
          founder_pricing: false,
          subscription_status: 'none',
          subscription_end: null,
          mfa_setup_prompted: false,
        });
        return;
      }

      const thisMonth = monthKey();
      const needsReset = !isSameMonth(data.scan_reset_date);
      setProfile({
        tier: (data.tier as Tier) || 'free',
        scan_count: needsReset ? 0 : data.scan_count || 0,
        scan_reset_date: thisMonth,
        founder_pricing: !!data.founder_pricing,
        subscription_status: data.subscription_status || 'none',
        subscription_end: data.subscription_end,
        mfa_setup_prompted: !!data.mfa_setup_prompted,
      });
    },
    [supabase],
  );

  useEffect(() => {
    if (!supabase) {
      setLoading(false);
      return;
    }

    let mounted = true;
    supabase.auth.getSession().then(({ data }) => {
      if (!mounted) return;
      setSession(data.session);
      setUser(data.session?.user ?? null);
      loadProfile(data.session?.user ?? null).finally(() => {
        if (mounted) setLoading(false);
      });
    });

    const { data: sub } = supabase.auth.onAuthStateChange((_event, newSession) => {
      setSession(newSession);
      setUser(newSession?.user ?? null);
      void loadProfile(newSession?.user ?? null);
    });

    return () => {
      mounted = false;
      sub.subscription.unsubscribe();
    };
  }, [supabase, loadProfile]);

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

  const bumpScanCount = useCallback(() => {
    setProfile((p) => (p ? { ...p, scan_count: p.scan_count + 1 } : p));
  }, []);

  const signUp = useCallback<AuthContextValue['signUp']>(
    async (email, password, ageVerified) => {
      if (!ageVerified) return { error: 'You must confirm you are 21 or older.' };
      if (!supabase) return { error: 'Auth is not configured. Set Supabase env vars.' };
      // A1 S3 — partner attribution: forward the first-touch vyndr_ref
      // cookie (set by PartnerRefCapture on a ?ref=CODE visit) into the
      // signup metadata. The internal /api/partners report reads it.
      const partnerRef = readRefCookie(document.cookie);
      const { error } = await supabase.auth.signUp({
        email,
        password,
        options: {
          emailRedirectTo: `${window.location.origin}/auth/callback`,
          ...(partnerRef ? { data: { partner_ref: partnerRef } } : {}),
        },
      });
      if (error) return { error: error.message };
      return {};
    },
    [supabase],
  );

  const signIn = useCallback<AuthContextValue['signIn']>(
    async (email, password) => {
      if (!supabase) return { error: 'Auth is not configured. Set Supabase env vars.' };
      const { error } = await supabase.auth.signInWithPassword({ email, password });
      if (error) return { error: error.message };
      return {};
    },
    [supabase],
  );

  // Session 13 — generic OAuth dispatcher. Supabase returns an error
  // object when the provider isn't configured in the dashboard
  // (Apple needs a Service ID + private key; Twitter/X needs an
  // OAuth 2.0 client). We translate the upstream error into a flat
  // `{ error: string }` shape so the login UI can show a friendly
  // line without inspecting Supabase internals.
  const signInWithProvider = useCallback<AuthContextValue['signInWithProvider']>(
    async (provider) => {
      if (!supabase) return { error: 'Auth not initialized' };
      try {
        const { error } = await supabase.auth.signInWithOAuth({
          provider,
          options: { redirectTo: `${window.location.origin}/auth/callback` },
        });
        if (error) {
          return { error: `${provider} login isn't available yet. Use email or another method.` };
        }
        return {};
      } catch {
        return { error: 'Login failed. Try another method.' };
      }
    },
    [supabase],
  );

  // Kept as a thin alias so legacy callers (signup/login pages) keep
  // working without churn. New code should call signInWithProvider.
  const signInWithGoogle = useCallback(async () => {
    await signInWithProvider('google');
  }, [signInWithProvider]);

  const signOut = useCallback(async () => {
    if (!supabase) return;
    await supabase.auth.signOut();
    setProfile(null);
  }, [supabase]);

  const markMFAPrompted = useCallback(async () => {
    if (!supabase || !user) return;
    setProfile((p) => (p ? { ...p, mfa_setup_prompted: true } : p));
    await supabase.from('user_profiles').update({ mfa_setup_prompted: true }).eq('id', user.id);
  }, [supabase, user]);

  const value = useMemo<AuthContextValue>(() => {
    const tier = profile?.tier ?? 'free';
    const scanCount = profile?.scan_count ?? 0;
    const scansRemaining = tier === 'free' ? Math.max(0, FREE_LIMIT - scanCount) : null;
    const canScan = tier !== 'free' || scansRemaining === null || (scansRemaining ?? 0) > 0;

    return {
      user,
      session,
      profile,
      tier,
      scanCount,
      scansRemaining,
      canScan,
      loading,
      signUp,
      signIn,
      signInWithGoogle,
      signInWithProvider,
      signOut,
      refresh,
      bumpScanCount,
      markMFAPrompted,
    };
  }, [user, session, profile, loading, signUp, signIn, signInWithGoogle, signInWithProvider, signOut, refresh, bumpScanCount, markMFAPrompted]);

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

export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) {
    return {
      user: null,
      session: null,
      profile: null,
      tier: 'free',
      scanCount: 0,
      scansRemaining: 5,
      canScan: true,
      loading: false,
      signUp: async () => ({ error: 'Auth not initialized' }),
      signIn: async () => ({ error: 'Auth not initialized' }),
      signInWithGoogle: async () => {},
      signInWithProvider: async () => ({ error: 'Auth not initialized' }),
      signOut: async () => {},
      refresh: async () => {},
      bumpScanCount: () => {},
      markMFAPrompted: async () => {},
    };
  }
  return ctx;
}
