"use client";

import { createContext, ReactNode, useCallback, useContext, useEffect, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import { apiGet, apiPost } from "@/lib/api/client";
import type { AdminUserProfile } from "@/lib/db/services/auth";
import { isPublicRoute } from "./protected-routes";

/**
 * MySQL-session-backed (see lib/auth/session.ts — a secure, httpOnly
 * cookie, never read/written from client JS). Replaces the old
 * Supabase-client version.
 *
 * Keeps the same external `{ user, profile, loading, refreshProfile,
 * signOut }` shape Topbar/Sidebar/settings already consume, so they
 * don't need to change how they read `user.email`, `profile?.full_name`.
 */

interface AuthUser {
  id: string;
  email: string;
}

interface AuthProfile {
  full_name: string | null;
  phone: string | null;
}

interface AuthContextValue {
  user: AuthUser | null;
  profile: AuthProfile | null;
  loading: boolean;
  refreshProfile: () => Promise<void>;
  signOut: () => Promise<void>;
}

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

function split(profile: AdminUserProfile | null): { user: AuthUser | null; profile: AuthProfile | null } {
  if (!profile) return { user: null, profile: null };
  return {
    user: { id: profile.id, email: profile.email },
    profile: { full_name: profile.fullName, phone: profile.phone },
  };
}

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [profile, setProfile] = useState<AuthProfile | null>(null);
  const [loading, setLoading] = useState(true);
  const router = useRouter();
  const pathname = usePathname();

  const refresh = useCallback(async () => {
    try {
      const { user: fetched } = await apiGet<{ user: AdminUserProfile | null }>("/api/auth/me");
      const split_ = split(fetched);
      setUser(split_.user);
      setProfile(split_.profile);
    } catch {
      // 401 (not signed in, or signed in but no longer a valid admin —
      // getAdminProfileById() returns null for a demoted/blocked
      // account, which requireAdmin() turns into a 401) is the
      // expected case here, not an error worth logging.
      setUser(null);
      setProfile(null);
    }
  }, []);

  useEffect(() => {
    let active = true;
    refresh().finally(() => active && setLoading(false));
    return () => {
      active = false;
    };
  }, [refresh]);

  // Safety net for what middleware's fast cookie-presence check can't
  // catch on its own (see middleware.ts): a cookie that's present but
  // names a session that's expired, revoked, or belongs to an account
  // that's been blocked or demoted from admin since it signed in. This
  // is the real, DB-backed check (GET /api/auth/me runs server-side in
  // the Node.js runtime) — once it resolves to "not a valid admin" on
  // a protected route, redirect.
  useEffect(() => {
    if (!loading && !user && !isPublicRoute(pathname ?? "")) {
      router.replace("/login");
    }
  }, [loading, user, pathname, router]);

  const value: AuthContextValue = {
    user,
    profile,
    loading,
    refreshProfile: refresh,
    signOut: async () => {
      try {
        await apiPost("/api/auth/logout");
      } catch {
        // Even if the request fails, still clear local state below —
        // the person clicked "log out" and expects to end up logged out.
      }
      setUser(null);
      setProfile(null);
    },
  };

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

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuth must be used within AuthProvider");
  return ctx;
}
