"use client";

import { createContext, ReactNode, useCallback, useContext, useEffect, useRef, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { apiGet, apiPost } from "@/lib/api/client";
import type { UserProfile } from "@/lib/backend/types";
import { isPublicRoute } from "./protected-routes";
import { useTranslation } from "@/lib/i18n/LanguageProvider";

/**
 * MySQL-session-backed (see lib/auth/session.ts server-side — 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 the rest of the app already consumes, so pages
 * outside the auth flow itself (home, orders, profile, notifications,
 * order-store, ...) don't need to change how they read `user.id`,
 * `user.email`, `profile?.full_name`, `profile?.created_at`.
 *
 * `user` and `profile` here are the same MySQL UserProfile split back
 * into two shapes for that reason — not two separate fetches like the
 * Supabase version needed (auth.users vs. public.profiles).
 */

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

interface AuthProfile {
  full_name: string | null;
  phone: string | null;
  created_at: string;
  is_admin: boolean;
  status: "active" | "blocked";
}

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: UserProfile | 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,
      created_at: profile.createdAt,
      is_admin: profile.isAdmin,
      status: profile.status,
    },
  };
}

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 { theme, setTheme } = useTheme();
  const { language, setLanguage } = useTranslation();
  // Only sync once per signed-in userId, not on every refreshProfile()
  // call elsewhere in the app (e.g. after saving the edit-profile
  // form) — avoids re-applying/re-pushing preferences redundantly.
  const syncedUserId = useRef<string | null>(null);

  const refresh = useCallback(async () => {
    try {
      const { user: fetched } = await apiGet<{ user: UserProfile }>("/api/auth/me");
      const split_ = split(fetched);
      setUser(split_.user);
      setProfile(split_.profile);

      if (syncedUserId.current !== fetched.id) {
        syncedUserId.current = fetched.id;
        if (fetched.languagePreference || fetched.themePreference) {
          // Returning on a new device/browser (or after a fresh
          // install) — the saved profile wins over whatever's
          // currently active locally.
          if (fetched.languagePreference) setLanguage(fetched.languagePreference);
          if (fetched.themePreference) setTheme(fetched.themePreference);
        } else {
          // This account has never saved a preference — push up
          // whatever's active right now (from onboarding, or the
          // defaults) so it's there next time they sign in anywhere.
          apiPost("/api/auth/preferences", {
            languagePreference: language,
            themePreference: theme === "light" ? "light" : "dark",
          }).catch(() => {
            // Non-critical — local preferences still work fine even
            // if this particular sync attempt fails.
          });
        }
      }
    } catch (err) {
      // 401 (not signed in) is the expected case for a logged-out
      // visitor — not an error worth logging, just "no session."
      setUser(null);
      setProfile(null);
      syncedUserId.current = null;
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [setLanguage, setTheme]);

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

  // Safety net for the case 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 actually expired, revoked, or
  // belongs to a now-blocked account. This is the real, DB-backed
  // check (GET /api/auth/me runs server-side in the Node.js runtime),
  // so once it resolves to "not signed in" on a protected route, redirect.
  useEffect(() => {
    if (!loading && !user && !isPublicRoute(pathname ?? "")) {
      const redirectUrl = new URL("/login", window.location.origin);
      redirectUrl.searchParams.set("redirect", pathname ?? "/home");
      router.replace(redirectUrl.pathname + redirectUrl.search);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [loading, user, pathname]);

  const value: AuthContextValue = {
    user,
    profile,
    loading,
    refreshProfile: refresh,
    signOut: async () => {
      try {
        await apiPost("/api/auth/logout");
      } catch {
        // Even if the request fails (network blip, already-expired
        // session), 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;
}
