import React, { createContext, useContext, useEffect, useState } from "react";
import { useDispatch } from "react-redux";

import { AuthAPI } from "@/apis/AuthAPI";
import {
  clearAuthToken,
  setAuthToken,
  setUnauthorizedHandler,
} from "@/apis/configs/axiosConfigs";
import { appParams } from "@/lib/app-params";
import { clearAuthState, setAuthRuntimeState, setAuthState, setAuthViewState } from "@/store/auth/authSlice";
import type { AppDispatch } from "@/store/store";
import { createAxiosClient } from "@base44/sdk/dist/utils/axios-client";

type AuthError = {
  type: string;
  message: string;
} | null;

type AuthUser = Record<string, unknown> | null;
type PublicSettings = Record<string, unknown> | null;
type CapabilityValue = {
  read?: boolean;
  write?: boolean;
};
type AuthViewsConfig = {
  role?: {
    id?: number;
    name?: string;
  };
  idcompany?: number;
  views?: Array<Record<string, unknown>>;
  capabilities?: Record<string, CapabilityValue>;
  version?: string;
} | null;

type AuthContextValue = {
  user: AuthUser;
  isAuthenticated: boolean;
  isLoadingAuth: boolean;
  isLoadingPublicSettings: boolean;
  authError: AuthError;
  appPublicSettings: PublicSettings;
  logout: (shouldRedirect?: boolean) => void;
  navigateToLogin: () => void;
  checkAppState: () => Promise<void>;
};

type ApiLikeError = {
  status?: number;
  message?: string;
  data?: {
    extra_data?: {
      reason?: string;
    };
    reason?: string;
  };
  response?: {
    status?: number;
    data?: {
      extra_data?: {
        reason?: string;
      };
      reason?: string;
    };
  };
};

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

const getReasonFromError = (error: unknown): string | undefined => {
  const err = error as ApiLikeError;
  return err?.response?.data?.extra_data?.reason ?? err?.response?.data?.reason;
};

const getStatusFromError = (error: unknown): number | undefined => {
  const err = error as ApiLikeError;
  return err?.response?.status ?? err?.status;
};

const getMessageFromError = (error: unknown, fallback: string): string => {
  const err = error as ApiLikeError;
  return err?.message || fallback;
};

const AUTH_RUT_STORAGE_KEY = "auth_rut";

export const AuthProvider = ({ children }: { children: React.ReactNode }): React.ReactElement => {
  const dispatch = useDispatch<AppDispatch>();
  const isApiAuthProvider = AuthAPI.isApiProvider();
  const [user, setUser] = useState<AuthUser>(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoadingAuth, setIsLoadingAuth] = useState(true);
  const [isLoadingPublicSettings, setIsLoadingPublicSettings] = useState(true);
  const [authError, setAuthError] = useState<AuthError>(null);
  const [appPublicSettings, setAppPublicSettings] = useState<PublicSettings>(null);

  useEffect(() => {
    void checkAppState();
  }, []);

  useEffect(() => {
    setUnauthorizedHandler(() => {
      if (typeof window !== "undefined" && window.location.pathname.startsWith("/login")) {
        return;
      }
      navigateToLogin();
    });

    return () => {
      setUnauthorizedHandler(null);
    };
  }, []);

  useEffect(() => {
    dispatch(
      setAuthRuntimeState({
        isLoadingAuth,
        isLoadingPublicSettings,
        authError,
        appPublicSettings,
      }),
    );
  }, [dispatch, isLoadingAuth, isLoadingPublicSettings, authError, appPublicSettings]);

  useEffect(() => {
    if (!isAuthenticated) {
      dispatch(clearAuthState());
      return;
    }

    const sessionToken = AuthAPI.getSessionToken();
    dispatch(
      setAuthState({
        user,
        token: sessionToken,
        isAuthenticated,
      }),
    );
    if (sessionToken) {
      setAuthToken(sessionToken);
    }
  }, [dispatch, isAuthenticated, user]);

  const checkAppState = async (): Promise<void> => {
    if (isApiAuthProvider) {
      try {
        setIsLoadingPublicSettings(false);
        setAuthError(null);
        setAppPublicSettings(null);

        if (AuthAPI.hasSession()) {
          await checkUserAuth();
        } else {
          setIsLoadingAuth(false);
          setIsAuthenticated(false);
        }
      } catch (error) {
        const reason = getReasonFromError(error);
        const status = getStatusFromError(error);

        if (reason) {
          setAuthError({
            type: reason,
            message: getMessageFromError(error, "Error en estado de autenticacion"),
          });
        } else if (status === 401 || status === 403) {
          setAuthError({
            type: "auth_required",
            message: "Authentication required",
          });
        } else {
          setAuthError({
            type: "unknown",
            message: getMessageFromError(error, "Failed to load auth state"),
          });
        }
        setIsLoadingAuth(false);
      } finally {
        setIsLoadingPublicSettings(false);
      }
      return;
    }

    try {
      setIsLoadingPublicSettings(true);
      setAuthError(null);

      const appClient = createAxiosClient({
        baseURL: `${appParams.serverUrl}/api/apps/public`,
        headers: {
          "X-App-Id": appParams.appId,
        },
        token: appParams.token,
        interceptResponses: true,
      });

      try {
        const publicSettings = (await appClient.get(
          `/prod/public-settings/by-id/${appParams.appId}`,
        )) as PublicSettings;
        setAppPublicSettings(publicSettings);

        if (appParams.token) {
          await checkUserAuth();
        } else {
          setIsLoadingAuth(false);
          setIsAuthenticated(false);
        }
        setIsLoadingPublicSettings(false);
      } catch (appError) {
        const err = appError as ApiLikeError;
        const reason = err?.data?.extra_data?.reason;

        if (err.status === 403 && reason) {
          if (reason === "auth_required") {
            setAuthError({
              type: "auth_required",
              message: "Authentication required",
            });
          } else if (reason === "user_not_registered") {
            setAuthError({
              type: "user_not_registered",
              message: "User not registered for this app",
            });
          } else {
            setAuthError({
              type: reason,
              message: getMessageFromError(appError, "Failed to load app"),
            });
          }
        } else {
          setAuthError({
            type: "unknown",
            message: getMessageFromError(appError, "Failed to load app"),
          });
        }
        setIsLoadingPublicSettings(false);
        setIsLoadingAuth(false);
      }
    } catch (error) {
      setAuthError({
        type: "unknown",
        message: getMessageFromError(error, "An unexpected error occurred"),
      });
      setIsLoadingPublicSettings(false);
      setIsLoadingAuth(false);
    }
  };

  const checkUserAuth = async (): Promise<void> => {
    try {
      setIsLoadingAuth(true);
      const currentUser = await AuthAPI.getCurrentUser<Record<string, unknown>>();
      const dynamicViewsEnabled = Boolean(
        (currentUser as { has_dynamic_views?: unknown })?.has_dynamic_views,
      );

      let viewsConfig: AuthViewsConfig = null;
      if (dynamicViewsEnabled) {
        try {
          viewsConfig = await AuthAPI.getViewsConfig<AuthViewsConfig>();
        } catch (viewsError) {
          console.warn("No se pudo obtener configuracion de vistas por rol", viewsError);
        }
      }

      dispatch(
        setAuthViewState({
          hasDynamicViews: dynamicViewsEnabled,
          viewsConfig,
        }),
      );

      setUser(currentUser);
      setIsAuthenticated(true);
      setIsLoadingAuth(false);
    } catch (error) {
      setIsLoadingAuth(false);
      setIsAuthenticated(false);
      dispatch(
        setAuthViewState({
          hasDynamicViews: false,
          viewsConfig: null,
        }),
      );

      const status = getStatusFromError(error);
      if (status === 401 || status === 403) {
        clearAuthToken();
        if (typeof window !== "undefined") {
          window.localStorage.removeItem(AUTH_RUT_STORAGE_KEY);
        }
        setAuthError({
          type: "auth_required",
          message: "Authentication required",
        });
      }
    }
  };

  const logout = (shouldRedirect = true): void => {
    setUser(null);
    setIsAuthenticated(false);
    dispatch(clearAuthState());
    clearAuthToken();

    void AuthAPI.logout({
      shouldRedirect,
      redirectTo: typeof window === "undefined" ? undefined : window.location.href,
    });
  };

  const navigateToLogin = (): void => {
    AuthAPI.redirectToLogin(
      typeof window === "undefined" ? undefined : window.location.href,
    );
  };

  return (
    <AuthContext.Provider
      value={{
        user,
        isAuthenticated,
        isLoadingAuth,
        isLoadingPublicSettings,
        authError,
        appPublicSettings,
        logout,
        navigateToLogin,
        checkAppState,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = (): AuthContextValue => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return context;
};
