'use client';

import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { loadSettings, persistSettings } from '@/utils/storage/settingsStorage';
import { oauthClientId } from '@/utils/env';

type AuthMethod = 'oauth' | 'manual';

interface StoredAuthState {
  token: string;
  namespace: string;
  method: AuthMethod;
}

export type AuthStatus = 'checking' | 'authenticated' | 'unauthenticated' | 'error';

interface AuthContextValue {
  status: AuthStatus;
  token: string | null;
  namespace: string | null;
  method: AuthMethod | null;
  error: string | null;
  oauthAvailable: boolean;
  loginWithOAuth: () => void;
  exchangeCodeForToken: (code: string, state: string) => Promise<boolean>;
  setManualToken: (token: string) => Promise<void>;
  logout: () => void;
}

const STORAGE_KEY = 'HF_AUTH_STATE';

const defaultValue: AuthContextValue = {
  status: 'checking',
  token: null,
  namespace: null,
  method: null,
  error: null,
  oauthAvailable: Boolean(oauthClientId),
  loginWithOAuth: () => {},
  exchangeCodeForToken: async () => false,
  setManualToken: async () => {},
  logout: () => {},
};

const AuthContext = createContext<AuthContextValue>(defaultValue);

async function validateToken(token: string) {
  const res = await fetch('/api/auth/hf/validate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ token }),
  });

  if (!res.ok) {
    const data = await res.json().catch(() => ({}));
    throw new Error(data?.error || 'Failed to validate token');
  }

  return res.json();
}

async function syncTokenWithSettings(token: string) {
  try {
    const current = await loadSettings();
    if (current.HF_TOKEN === token) {
      return;
    }
    current.HF_TOKEN = token;
    await persistSettings(current);
  } catch (error) {
    console.warn('Failed to persist HF token to settings:', error);
  }
}

async function clearTokenFromSettings() {
  try {
    const current = await loadSettings();
    if (current.HF_TOKEN !== '') {
      current.HF_TOKEN = '';
      await persistSettings(current);
    }
  } catch (error) {
    console.warn('Failed to clear HF token from settings:', error);
  }
}

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [status, setStatus] = useState<AuthStatus>('checking');
  const [token, setToken] = useState<string | null>(null);
  const [namespace, setNamespace] = useState<string | null>(null);
  const [method, setMethod] = useState<AuthMethod | null>(null);
  const [error, setError] = useState<string | null>(null);

  const oauthAvailable = Boolean(oauthClientId);

  const applyAuthState = useCallback(async ({ token: nextToken, namespace: nextNamespace, method: nextMethod }: StoredAuthState) => {
    setToken(nextToken);
    setNamespace(nextNamespace);
    setMethod(nextMethod);
    setStatus('authenticated');
    setError(null);

    if (typeof window !== 'undefined') {
      window.localStorage.setItem(
        STORAGE_KEY,
        JSON.stringify({
          token: nextToken,
          namespace: nextNamespace,
          method: nextMethod,
        }),
      );
    }

    syncTokenWithSettings(nextToken).catch(err => {
      console.warn('Failed to sync HF token with settings:', err);
    });
  }, []);

  const clearAuthState = useCallback(async () => {
    setToken(null);
    setNamespace(null);
    setMethod(null);
    setStatus('unauthenticated');
    setError(null);

    if (typeof window !== 'undefined') {
      window.localStorage.removeItem(STORAGE_KEY);
    }

    clearTokenFromSettings().catch(err => {
      console.warn('Failed to clear HF token from settings:', err);
    });
  }, []);

  // Restore stored token on mount
  useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    const restore = async () => {
      const raw = window.localStorage.getItem(STORAGE_KEY);
      if (!raw) {
        setStatus('unauthenticated');
        return;
      }

      try {
        const stored: StoredAuthState = JSON.parse(raw);
        if (!stored?.token) {
          setStatus('unauthenticated');
          return;
        }
        setStatus('checking');
        const data = await validateToken(stored.token);
        await applyAuthState({
          token: stored.token,
          namespace: data?.name || data?.preferred_username || stored.namespace || 'user',
          method: stored.method || 'manual',
        });
      } catch (err) {
        console.warn('Stored HF token invalid:', err);
        await clearAuthState();
      }
    };

    restore();
  }, [applyAuthState, clearAuthState]);

  const setManualToken = useCallback(
    async (manualToken: string) => {
      if (!manualToken) {
        setError('Please provide a token');
        setStatus('error');
        return;
      }
      setStatus('checking');
      setError(null);
      try {
        const data = await validateToken(manualToken);
        await applyAuthState({
          token: manualToken,
          namespace: data?.name || data?.preferred_username || 'user',
          method: 'manual',
        });
      } catch (err: any) {
        setError(err?.message || 'Failed to validate token');
        setStatus('error');
      }
    },
    [applyAuthState],
  );

  const exchangeCodeForToken = useCallback(
    async (code: string, state: string) => {
      if (!code || !state) {
        setError('Invalid authorization response.');
        setStatus('error');
        return false;
      }
      setStatus('checking');
      setError(null);
      try {
        const res = await fetch('/api/auth/hf/exchange', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          credentials: 'same-origin',
          body: JSON.stringify({ code, state }),
        });

        if (!res.ok) {
          const data = await res.json().catch(() => ({}));
          throw new Error(data?.error || 'Failed to exchange authorization code');
        }

        const data = await res.json();
        await applyAuthState({
          token: data.token,
          namespace: data.namespace || 'user',
          method: 'oauth',
        });
        return true;
      } catch (err: any) {
        setError(err?.message || 'Failed to authenticate with Hugging Face');
        setStatus('error');
        return false;
      }
    },
    [applyAuthState],
  );

  const loginWithOAuth = useCallback(() => {
    if (typeof window === 'undefined') {
      return;
    }
    if (!oauthAvailable) {
      setError('OAuth is not available on this deployment.');
      setStatus('error');
      return;
    }
    setStatus('checking');
    setError(null);

    window.location.href = '/api/auth/hf/login';
  }, []);

  const logout = useCallback(() => {
    clearAuthState();
  }, [clearAuthState]);

  const value = useMemo<AuthContextValue>(
    () => ({
      status,
      token,
      namespace,
      method,
      error,
      oauthAvailable,
      loginWithOAuth,
      exchangeCodeForToken,
      setManualToken,
      logout,
    }),
    [status, token, namespace, method, error, oauthAvailable, loginWithOAuth, exchangeCodeForToken, setManualToken, logout],
  );

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

export function useAuth() {
  return useContext(AuthContext);
}
