"use client";

import {
  createContext,
  useContext,
  useCallback,
  useEffect,
  type ReactNode,
} from "react";
import type { User } from "./types";
import { AUTH_UNAUTHENTICATED_EVENT, apiRequest, apiUrl } from "./api";
import { getLocalizedApiMessage } from "./api-message";
import { useAuthStore } from "./auth-store";

type ValidationErrors = Record<string, string[]>;

type LoginSuccessData = {
  user?: {
    id?: string | number;
    uuid?: string;
    name?: string;
    full_name?: string;
    first_name?: string;
    last_name?: string;
    guide_name?: string;
    email?: string;
    status?: string;
    reject_reason?: string | null;
    rejected_reason?: string | null;
    rejection_reason?: string | null;
  };
  admin?: {
    id?: string | number;
    uuid?: string;
    name?: string;
    full_name?: string;
    first_name?: string;
    last_name?: string;
    guide_name?: string;
    email?: string;
    status?: string;
    reject_reason?: string | null;
    rejected_reason?: string | null;
    rejection_reason?: string | null;
  };
  guide?: {
    id?: string | number;
    uuid?: string;
    name?: string;
    full_name?: string;
    first_name?: string;
    last_name?: string;
    guide_name?: string;
    email?: string;
    status?: string;
    reject_reason?: string | null;
    rejected_reason?: string | null;
    rejection_reason?: string | null;
  };
  role?: string;
  token?: string;
  id?: string | number;
  uuid?: string;
  name?: string;
  full_name?: string;
  first_name?: string;
  last_name?: string;
  guide_name?: string;
  email?: string;
  status?: string;
  reject_reason?: string | null;
  rejected_reason?: string | null;
  rejection_reason?: string | null;
};

interface LoginApiResponse {
  statusCode: number;
  success: boolean;
  data: "" | LoginSuccessData | ValidationErrors;
  message: string;
  message_hi?: string;
}

interface LoginResult {
  success: boolean;
  user?: User;
  message?: string;
  message_hi?: string;
  errors?: ValidationErrors;
  rejectedAccount?: {
    uuid?: string;
    name: string;
    email: string;
    reason: string;
  };
}

interface LogoutApiResponse {
  statusCode: number;
  success: boolean;
  data: "" | [];
  message: string;
  message_hi?: string;
}

interface LogoutResult {
  success: boolean;
  message: string;
}

interface AuthContextType {
  user: User | null;
  token: string | null;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<LoginResult>;
  logout: () => Promise<LogoutResult>;
  updateUser: (updates: Partial<User>) => void;
  isAuthenticated: boolean;
  isAdmin: boolean;
  isGuide: boolean;
}

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

function isLoginSuccessData(
  data: LoginApiResponse["data"],
): data is LoginSuccessData {
  return (
    !!data &&
    typeof data === "object" &&
    !Array.isArray(data) &&
    ("token" in data || "admin" in data || "guide" in data || "user" in data)
  );
}

function isValidationErrors(
  data: LoginApiResponse["data"],
): data is ValidationErrors {
  return (
    !!data &&
    typeof data === "object" &&
    !Array.isArray(data) &&
    !isLoginSuccessData(data) &&
    Object.values(data).every((value) => Array.isArray(value))
  );
}

function getRejectedAccount(data: LoginApiResponse["data"]) {
  if (!data || typeof data !== "object" || Array.isArray(data)) {
    return undefined;
  }

  const loginData = data as LoginSuccessData;
  const account =
    loginData.guide || loginData.admin || loginData.user || loginData;
  const status = String(account.status || loginData.status || "").toLowerCase();

  if (status !== "rejected") {
    return undefined;
  }

  return {
    uuid: String(
      account.uuid || loginData.uuid || account.id || loginData.id || "",
    ),
    name: getAccountName(account, loginData) || "Guide",
    email: account.email || loginData.email || "",
    reason:
      account.reject_reason ||
      account.rejected_reason ||
      account.rejection_reason ||
      loginData.reject_reason ||
      loginData.rejected_reason ||
      loginData.rejection_reason ||
      "No rejection reason was provided by admin.",
  };
}

function mapApiRole(
  role: string | undefined,
  data: LoginSuccessData,
): User["role"] {
  const normalizedRole = role?.toLowerCase().replace(/[\s_-]/g, "");

  if (
    normalizedRole === "guide" ||
    normalizedRole === "tourguide" ||
    normalizedRole === "touristguide"
  ) {
    return "guide";
  }

  if (
    normalizedRole === "admin" ||
    normalizedRole === "administrator" ||
    normalizedRole === "superadmin"
  ) {
    return "admin";
  }

  return data.guide || data.user ? "guide" : "admin";
}

function makeDisplayName(email: string, role: User["role"], name?: string) {
  if (name?.trim()) return name.trim();
  if (role === "admin") return "Admin User";
  return email.split("@")[0] || "Guide";
}

function getAccountName(
  account:
    | LoginSuccessData["guide"]
    | LoginSuccessData["admin"]
    | LoginSuccessData["user"]
    | undefined,
  data: LoginSuccessData,
) {
  const source = account || data;
  const firstLastName = [source?.first_name, source?.last_name]
    .filter(Boolean)
    .join(" ")
    .trim();

  return (
    source?.name ||
    source?.full_name ||
    ("guide_name" in (source || {}) ? source?.guide_name : undefined) ||
    firstLastName ||
    data.name ||
    data.full_name ||
    data.guide_name ||
    [data.first_name, data.last_name].filter(Boolean).join(" ").trim()
  );
}

function getAccountEmail(
  account:
    | LoginSuccessData["guide"]
    | LoginSuccessData["admin"]
    | LoginSuccessData["user"]
    | undefined,
  data: LoginSuccessData,
  fallbackEmail: string,
) {
  return account?.email || data.email || fallbackEmail;
}

function getAccountId(
  account:
    | LoginSuccessData["guide"]
    | LoginSuccessData["admin"]
    | LoginSuccessData["user"]
    | undefined,
  data: LoginSuccessData,
  fallbackId: string,
) {
  return String(
    account?.uuid || data.uuid || account?.id || data.id || fallbackId,
  );
}

export function AuthProvider({ children }: { children: ReactNode }) {
  const user = useAuthStore((state) => state.user);
  const token = useAuthStore((state) => state.token);
  const isLoading = useAuthStore((state) => state.isLoading);
  const setUser = useAuthStore((state) => state.setUser);
  const setIsLoading = useAuthStore((state) => state.setIsLoading);
  const setSession = useAuthStore((state) => state.setSession);
  const clearAuthStoreSession = useAuthStore((state) => state.clearSession);

  useEffect(() => {
    const savedToken = localStorage.getItem("auth_token");
    const savedUser = localStorage.getItem("auth_user");

    let nextUser: User | null = null;

    if (savedUser) {
      try {
        nextUser = JSON.parse(savedUser) as User;
      } catch {
        localStorage.removeItem("auth_user");
      }
    }

    setSession({ user: nextUser, token: savedToken || null });
    setIsLoading(false);
  }, [setIsLoading, setSession]);

  const login = useCallback(
    async (email: string, password: string): Promise<LoginResult> => {
      setIsLoading(true);

      try {
        const response = await apiRequest(apiUrl("/login"), {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
          },
          body: JSON.stringify({ email, password }),
        });

        const result = (await response.json()) as LoginApiResponse;
        const rejectedAccount = getRejectedAccount(result.data);

        if (rejectedAccount) {
          return {
            success: false,
            message: result.message || "Your account was rejected by admin.",
            message_hi: result.message_hi,
            rejectedAccount,
          };
        }

        if (!response.ok || !result.success) {
          return {
            success: false,
            message: result.message || "Login failed. Please try again.",
            message_hi: result.message_hi,
            errors: isValidationErrors(result.data) ? result.data : undefined,
          };
        }

        if (!isLoginSuccessData(result.data)) {
          return {
            success: false,
            message: "Login failed. Invalid server response.",
          };
        }

        const role = mapApiRole(result.data.role, result.data);
        const account =
          role === "guide"
            ? result.data.guide || result.data.user
            : result.data.admin || result.data.user;
        const accountEmail = getAccountEmail(account, result.data, email);
        const accountId = getAccountId(account, result.data, email);
        const nextUser: User = {
          id: accountId,
          email: accountEmail,
          name: makeDisplayName(
            accountEmail,
            role,
            getAccountName(account, result.data),
          ),
          role,
          guideId: role === "guide" ? accountId : undefined,
        };

        const nextToken = result.data.token || null;

        if (nextToken) {
          localStorage.setItem("auth_token", nextToken);
        }
        localStorage.setItem("auth_user", JSON.stringify(nextUser));
        setSession({ user: nextUser, token: nextToken });

        return {
          success: true,
          user: nextUser,
          message: result.message,
          message_hi: result.message_hi,
        };
      } catch {
        return {
          success: false,
          message: "Unable to connect to the login server. Please try again.",
        };
      } finally {
        setIsLoading(false);
      }
    },
    [setIsLoading, setSession],
  );

  const clearSession = useCallback(() => {
    localStorage.removeItem("auth_token");
    localStorage.removeItem("auth_user");
    clearAuthStoreSession();
  }, [clearAuthStoreSession]);

  useEffect(() => {
    const handleUnauthenticated = () => {
      clearSession();
      setIsLoading(false);
    };

    window.addEventListener(AUTH_UNAUTHENTICATED_EVENT, handleUnauthenticated);

    return () => {
      window.removeEventListener(
        AUTH_UNAUTHENTICATED_EVENT,
        handleUnauthenticated,
      );
    };
  }, [clearSession, setIsLoading]);

  const updateUser = useCallback((updates: Partial<User>) => {
    setUser((currentUser) => {
      if (!currentUser) {
        return currentUser;
      }

      const nextUser = { ...currentUser, ...updates };
      localStorage.setItem("auth_user", JSON.stringify(nextUser));
      return nextUser;
    });
  }, []);

  const logout = useCallback(async (): Promise<LogoutResult> => {
    setIsLoading(true);

    try {
      const activeToken = token || localStorage.getItem("auth_token");
      const response = await apiRequest(apiUrl("/logout"), {
        method: "POST",
        headers: {
          Authorization: `Bearer ${activeToken || ""}`,
          Accept: "application/json",
        },
      });

      const result = (await response.json()) as LogoutApiResponse;
      clearSession();

      return {
        success: response.ok && result.success,
        message: getLocalizedApiMessage(result, "Logout completed."),
      };
    } catch {
      clearSession();

      return {
        success: false,
        message: "Unable to connect to the logout server.",
      };
    } finally {
      setIsLoading(false);
    }
  }, [clearSession, token]);

  return (
    <AuthContext.Provider
      value={{
        user,
        token,
        isLoading,
        login,
        logout,
        updateUser,
        isAuthenticated: !!user,
        isAdmin: user?.role === "admin",
        isGuide: user?.role === "guide",
      }}
    >
      {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;
}
