import { useState } from 'react';
import { useSetAtom } from 'jotai';
import { useQueryClient } from '@tanstack/react-query';
import { z } from 'zod';
import { authAtom, type AuthUser } from '../store/authAtom';
import { getApiBase, saveToken, removeToken } from '../services/apiClient';

// ---------------------------------------------------------------------------
// Password strength schema — same rules as the backend
// ---------------------------------------------------------------------------
export const PasswordSchema = z
  .string()
  .min(8, 'Minimum 8 characters')
  .regex(/[A-Z]/, 'Must contain an uppercase letter')
  .regex(/[a-z]/, 'Must contain a lowercase letter')
  .regex(/[0-9]/, 'Must contain a digit')
  .regex(/[^A-Za-z0-9]/, 'Must contain a special character');

// ---------------------------------------------------------------------------
// JWT decode helper (no library needed — just base64)
// ---------------------------------------------------------------------------
function decodeJwtPayload(token: string): { sub: string; email: string; exp: number } {
  const base64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
  return JSON.parse(atob(base64));
}

function tokenToUser(token: string): AuthUser {
  const payload = decodeJwtPayload(token);
  return { userId: payload.sub, email: payload.email, token };
}

// ---------------------------------------------------------------------------
// Hook
// ---------------------------------------------------------------------------

/**
 * useAuth — provides all authentication operations.
 * Manages loading and error state locally.
 * Platform-agnostic: uses getApiBase() + injected tokenStorage.
 */
export function useAuth() {
  const setAuth = useSetAtom(authAtom);
  const queryClient = useQueryClient();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  function clearError() {
    setError(null);
  }

  async function post(path: string, body: unknown): Promise<unknown> {
    // Abort after 10 seconds to prevent the spinner from hanging indefinitely
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), 10_000);

    let response: Response;
    try {
      response = await fetch(`${getApiBase()}${path}`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
        signal: controller.signal,
      });
    } catch (e) {
      const isTimeout = e instanceof Error && e.name === 'AbortError';
      throw new Error(
        isTimeout
          ? 'Request timed out — check your connection'
          : 'Network error — server unreachable',
      );
    } finally {
      clearTimeout(timer);
    }

    const data: unknown = await response.json().catch(() => null);
    if (!response.ok) {
      const message = (data as { message?: string })?.message ?? `Error ${response.status}`;
      throw new Error(Array.isArray(message) ? message.join(', ') : message);
    }
    return data;
  }

  async function login(email: string, password: string): Promise<void> {
    setIsLoading(true);
    setError(null);
    try {
      const data = (await post('/auth/login', { email, password })) as { accessToken: string };
      await saveToken(data.accessToken);
      setAuth(tokenToUser(data.accessToken));
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Login failed');
    } finally {
      setIsLoading(false);
    }
  }

  async function register(email: string, password: string): Promise<boolean> {
    // Validate password strength client-side before hitting the API
    const result = PasswordSchema.safeParse(password);
    if (!result.success) {
      setError(result.error.errors[0].message);
      return false;
    }
    setIsLoading(true);
    setError(null);
    try {
      await post('/auth/register', { email, password });
      return true;
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Registration failed');
      return false;
    } finally {
      setIsLoading(false);
    }
  }

  async function verifyEmail(email: string, code: string): Promise<boolean> {
    setIsLoading(true);
    setError(null);
    try {
      const data = (await post('/auth/verify-email', { email, code })) as { accessToken: string };
      await saveToken(data.accessToken);
      setAuth(tokenToUser(data.accessToken));
      return true;
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Verification failed');
      return false;
    } finally {
      setIsLoading(false);
    }
  }

  async function resendOtp(email: string): Promise<void> {
    setIsLoading(true);
    setError(null);
    try {
      await post('/auth/resend-otp', { email });
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to resend code');
    } finally {
      setIsLoading(false);
    }
  }

  async function forgotPassword(email: string): Promise<void> {
    setIsLoading(true);
    setError(null);
    try {
      await post('/auth/forgot-password', { email });
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Request failed');
    } finally {
      setIsLoading(false);
    }
  }

  async function resetPassword(email: string, code: string, newPassword: string): Promise<boolean> {
    const result = PasswordSchema.safeParse(newPassword);
    if (!result.success) {
      setError(result.error.errors[0].message);
      return false;
    }
    setIsLoading(true);
    setError(null);
    try {
      await post('/auth/reset-password', { email, code, newPassword });
      return true;
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Reset failed');
      return false;
    } finally {
      setIsLoading(false);
    }
  }

  async function logout(): Promise<void> {
    await removeToken();
    setAuth(null);
    queryClient.clear();
  }

  return {
    isLoading,
    error,
    clearError,
    login,
    register,
    verifyEmail,
    resendOtp,
    forgotPassword,
    resetPassword,
    logout,
  };
}
