'use client';

import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react';
import { authClient } from '@/lib/auth-client';
import { fetchApi } from '@/lib/api/client';

interface User {
  id: string;
  email: string;
  name: string;
  role: string;
  phone?: string;
  languagePreference?: string;
  isClaimed?: boolean;
  rucNumber?: string;
  accountStatus?: string;
}

interface AuthContextType {
  user: User | null;
  /** Always null: auth moved to httpOnly session cookies (Better Auth). Kept for compat. */
  token: string | null;
  isLoading: boolean;
  isAdmin: boolean;
  hasAdminAccess: boolean;
  login: (email: string, password: string) => Promise<void>;
  loginWithGoogle: (credential: string) => Promise<void>;
  loginWithMagicLink: (token: string) => Promise<User | null>;
  register: (data: RegisterData) => Promise<void>;
  logout: () => void;
  updateUser: (user: User) => void;
  setAuthData: (data: { user: User; token?: string }) => void;
  refreshUser: () => Promise<User | null>;
}

interface RegisterData {
  email: string;
  password: string;
  name: string;
  phone?: string;
  languagePreference?: string;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

// Legacy storage from the pre-Better-Auth JWT era; cleared once on mount.
const LEGACY_TOKEN_KEY = 'spanglish-token';
const LEGACY_USER_KEY = 'spanglish-user';
const LEGACY_AUTH_COOKIE = 'spanglish-auth';

function mapSessionUser(sessionUser: any): User {
  return {
    id: sessionUser.id,
    email: sessionUser.email,
    name: sessionUser.name,
    role: sessionUser.role ?? 'user',
    phone: sessionUser.phone ?? undefined,
    languagePreference: sessionUser.languagePreference ?? undefined,
    isClaimed: Boolean(sessionUser.isClaimed ?? true),
    rucNumber: sessionUser.rucNumber ?? undefined,
    accountStatus: sessionUser.accountStatus ?? 'active',
  };
}

function messageFrom(error: { message?: string; code?: string; status?: number } | null, fallback: string): string {
  return error?.message || fallback;
}

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  const refreshUser = useCallback(async (): Promise<User | null> => {
    try {
      const { data } = await authClient.getSession();
      if (data?.user) {
        const mapped = mapSessionUser(data.user);
        setUser(mapped);
        return mapped;
      }
      setUser(null);
      return null;
    } catch (error) {
      // Network error: keep current state
      console.error('Failed to refresh user data:', error);
      return null;
    }
  }, []);

  useEffect(() => {
    // One-time cleanup of legacy JWT-era storage
    try {
      localStorage.removeItem(LEGACY_TOKEN_KEY);
      localStorage.removeItem(LEGACY_USER_KEY);
      document.cookie = `${LEGACY_AUTH_COOKIE}=; path=/; max-age=0; SameSite=Lax`;
    } catch {
      /* SSR / storage unavailable */
    }

    refreshUser().finally(() => setIsLoading(false));
  }, [refreshUser]);

  const login = async (email: string, password: string) => {
    const { error } = await authClient.signIn.email({ email, password });
    if (error) {
      throw new Error(messageFrom(error, 'Login failed'));
    }
    await refreshUser();
  };

  const loginWithGoogle = async (credential: string) => {
    const { error } = await authClient.signIn.social({
      provider: 'google',
      idToken: { token: credential },
    });
    if (error) {
      // Carry the code through: GoogleSignInButton turns OAUTH_LINK_ERROR into
      // something actionable instead of showing better-auth's bare
      // "account not linked".
      const err = new Error(messageFrom(error, 'Google login failed'));
      (err as Error & { code?: string }).code = error.code;
      throw err;
    }
    await refreshUser();
  };

  const loginWithMagicLink = async (magicToken: string): Promise<User | null> => {
    const { error } = await authClient.magicLink.verify({ query: { token: magicToken } });
    if (error) {
      throw new Error(messageFrom(error, 'Invalid or expired link'));
    }
    return refreshUser();
  };

  const register = async (registerData: RegisterData) => {
    const { error } = await authClient.signUp.email({
      email: registerData.email,
      password: registerData.password,
      name: registerData.name,
      phone: registerData.phone || undefined,
      languagePreference: registerData.languagePreference || undefined,
    } as any);
    if (error) {
      // Existing-but-unclaimed accounts (created during guest booking) should
      // point the user to the claim flow instead of a bare "already exists".
      if (error.code === 'USER_ALREADY_EXISTS') {
        try {
          const { canClaim } = await fetchApi<{ canClaim: boolean }>(
            `/api/auth-ext/claim-eligibility?email=${encodeURIComponent(registerData.email)}`
          );
          if (canClaim) {
            const err = new Error(
              'This email has an unclaimed account from a previous booking. Use "Email Link" on the login page to claim it.'
            );
            (err as any).canClaim = true;
            throw err;
          }
        } catch (e: any) {
          if (e?.canClaim) throw e;
          /* eligibility check failed: fall through to generic message */
        }
      }
      throw new Error(messageFrom(error, 'Registration failed'));
    }
    await refreshUser();
  };

  const logout = useCallback(() => {
    // Best-effort server-side revocation; local state clears regardless.
    authClient.signOut().catch(() => {
      /* ignore network errors */
    });
    setUser(null);
  }, []);

  const updateUser = useCallback((updatedUser: User) => {
    setUser(updatedUser);
  }, []);

  // Compat shim for callers that used to push {user, token} after custom auth
  // flows; the session cookie is already set by then, so only state updates.
  const setAuthData = useCallback((data: { user: User; token?: string }) => {
    setUser(data.user);
  }, []);

  const isAdmin = user?.role === 'admin' || user?.role === 'organizer';
  const hasAdminAccess = user?.role === 'admin' || user?.role === 'organizer' || user?.role === 'staff' || user?.role === 'marketing';

  return (
    <AuthContext.Provider
      value={{
        user,
        token: null,
        isLoading,
        isAdmin,
        hasAdminAccess,
        login,
        loginWithGoogle,
        loginWithMagicLink,
        register,
        logout,
        updateUser,
        setAuthData,
        refreshUser,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
