import React, { createContext, useContext, useEffect, useRef, useState } from "react";
import { demoUsers } from "../data/mockAppData";
import { supabase, hasSupabaseConfig } from "../supabaseClient";
import { logError } from "../utils/errorLogger";

const AuthContext = createContext(null);
const STORAGE_KEY = "construction-auth-local-user";
const SIGNED_OUT_FLAG = "supersam-signed-out";

const encodeLocalAuth = (data) => {
  try {
    return btoa(encodeURIComponent(JSON.stringify(data)));
  } catch {
    return null;
  }
};

const decodeLocalAuth = (raw) => {
  try {
    return JSON.parse(decodeURIComponent(atob(raw)));
  } catch {
    return null;
  }
};
export const PROFILE_LOAD_ERROR = "Не удалось загрузить профиль пользователя.";
export const UNKNOWN_EMAIL_ERROR = "Email не найден в системе. Обратитесь к администратору.";

const UNKNOWN_EMAIL_ERROR_PATTERNS = [
  /user not found/i,
  /email not found/i,
  /user does not exist/i,
  /invalid login credentials/i,
  /signup is disabled/i,
  /sign up is disabled/i,
  /signups not allowed/i,
  /email not registered/i,
  /email address is not verified/i,
];

const STALE_REFRESH_TOKEN_PATTERNS = [
  /invalid refresh token/i,
  /refresh token not found/i,
];

export const normalizeOtpError = (error) => {
  const message = error instanceof Error ? error.message : String(error || "");
  if (UNKNOWN_EMAIL_ERROR_PATTERNS.some((pattern) => pattern.test(message))) {
    return new Error(UNKNOWN_EMAIL_ERROR);
  }

  return error instanceof Error ? error : new Error(message || PROFILE_LOAD_ERROR);
};

export const isStaleRefreshTokenError = (error) => {
  const message = error instanceof Error ? error.message : String(error || "");
  return STALE_REFRESH_TOKEN_PATTERNS.some((pattern) => pattern.test(message));
};

export const buildOtpRequestPayload = (email) => ({
  email,
  options: {
    shouldCreateUser: false,
  },
});

export const resolveDemoUser = (email, roleHint) => {
  return (
    demoUsers.find((user) => user.role === roleHint) ||
    demoUsers.find((user) => user.email === email) ||
    demoUsers[0]
  );
};

export const mapProfileToAuthUser = (profile) => {
  if (!profile) {
    return null;
  }

  const roleInfo = Array.isArray(profile.role_info) ? profile.role_info[0] : profile.role_info;

  return {
    id: profile.id,
    email: profile.email,
    name: profile.name,
    role: roleInfo?.name || "manager",
    lastLogin: profile.last_login,
  };
};

export const mapSessionUserToAuthUser = (sessionUser) => {
  if (!sessionUser) {
    return null;
  }

  const userMetadata = sessionUser.user_metadata || {};
  const appMetadata = sessionUser.app_metadata || {};

  return {
    id: sessionUser.id,
    email: sessionUser.email,
    name: userMetadata.name || sessionUser.email || "Пользователь",
    role: userMetadata.role || appMetadata.role || null,
    lastLogin: sessionUser.last_sign_in_at || sessionUser.updated_at || null,
  };
};

export const fetchUserProfile = async (userId) => {
  if (!supabase || !userId) return null;
  const { data, error } = await supabase
    .from("users")
    .select("id, email, name, role_id, last_login, roles(name)")
    .eq("id", userId)
    .maybeSingle();
  if (error || !data) return null;
  return {
    id: data.id,
    email: data.email,
    name: data.name,
    role_info: data.roles,
    last_login: data.last_login,
  };
};

/** Check if user explicitly signed out (flag survives page refresh via sessionStorage) */
const isSignedOut = () => sessionStorage.getItem(SIGNED_OUT_FLAG) === "1";

/** Clear ALL auth state from storage — called on explicit signOut */
const clearAllAuthStorage = () => {
  // Clear Supabase secureStorage keys from localStorage
  localStorage.removeItem("supersam-auth");
  localStorage.removeItem("supersam-ak");
  // Clear local auth cache from localStorage
  localStorage.removeItem(STORAGE_KEY);
  localStorage.removeItem("construction-auth-role-hint");
  // Set signed-out flag so page refresh doesn't auto-restore session
  sessionStorage.setItem(SIGNED_OUT_FLAG, "1");
};

export const AuthProvider = ({ children }) => {
  // Supabase mode: always start null, session restore via onAuthStateChange
  // Demo mode: restore from localStorage
  const [user, setUser] = useState(() => {
    if (hasSupabaseConfig) return null;
    const stored = localStorage.getItem(STORAGE_KEY);
    return stored ? decodeLocalAuth(stored) : null;
  });
  const [pendingEmail, setPendingEmail] = useState("");
  const [isOtpSent, setIsOtpSent] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [authError, setAuthError] = useState("");
  // Track whether the initial session restore from Supabase has completed
  const [isSessionLoading, setIsSessionLoading] = useState(() => !!(hasSupabaseConfig && supabase));

  // Ref to prevent getSession from restoring session after explicit signOut
  const signedOutRef = useRef(false);

  useEffect(() => {
    if (!hasSupabaseConfig || !supabase) {
      return undefined;
    }

    // Track whether getSession() has resolved — onAuthStateChange's INITIAL_SESSION
    // can fire with null before storage has been read, causing premature redirect.
    // Only onAuthStateChange should update user AFTER initial load is complete.
    let getSessionResolved = false;

    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((event, session) => {
      // During initial load, ignore null sessions from onAuthStateChange —
      // getSession() is the authoritative source. SIGNED_OUT events are always valid.
      if (!session?.user) {
        if (!getSessionResolved && event === "INITIAL_SESSION") {
          // Don't set user=null or isSessionLoading=false yet — let getSession() decide.
          return;
        }
        setUser(null);
        setAuthError("");
        window.__supersam_user_id__ = null;
        setIsSessionLoading(false);
        return;
      }

      // Block session restore if user explicitly signed out (ref or sessionStorage flag)
      if (signedOutRef.current || isSignedOut()) {
        setIsSessionLoading(false);
        return;
      }

      const baseUser = mapSessionUserToAuthUser(session.user);
      // Expose userId for error logger
      window.__supersam_user_id__ = session.user?.id || null;
      if (baseUser) {
        fetchUserProfile(session.user.id).then((profile) => {
          if (profile) {
            setUser(mapProfileToAuthUser(profile));
          } else {
            setUser({ ...baseUser, role: baseUser.role || "manager" });
          }
          setIsSessionLoading(false);
        });
      } else {
        setUser(null);
        setIsSessionLoading(false);
      }
      setAuthError("");
    });

    supabase.auth.getSession().then(({ data, error }) => {
      getSessionResolved = true;
      if (error && isStaleRefreshTokenError(error)) {
        setUser(null);
        setAuthError("Сессия истекла. Войдите заново.");
        clearAllAuthStorage();
        void supabase.auth.signOut({ scope: "local" });
        setIsSessionLoading(false);
        return;
      }

      // Block session restore if user explicitly signed out (ref or sessionStorage flag)
      if (signedOutRef.current || isSignedOut()) {
        setIsSessionLoading(false);
        return;
      }

      if (data.session?.user) {
        const baseUser = mapSessionUserToAuthUser(data.session.user);
        if (baseUser) {
          fetchUserProfile(data.session.user.id).then((profile) => {
            if (profile) {
              setUser(mapProfileToAuthUser(profile));
            } else {
              setUser({ ...baseUser, role: baseUser.role || "manager" });
            }
            setIsSessionLoading(false);
          });
        } else {
          setIsSessionLoading(false);
        }
      } else {
        setIsSessionLoading(false);
      }
    }).catch(() => {
      // getSession rejected — ensure we don't hang forever
      setIsSessionLoading(false);
    });

    return () => subscription.unsubscribe();
  }, []);

  useEffect(() => {
    if (user && isDemoMode) {
      const encoded = encodeLocalAuth(user); if (encoded) localStorage.setItem(STORAGE_KEY, encoded);
    }
    if (!user && isDemoMode) {
      localStorage.removeItem(STORAGE_KEY);
    }
  }, [user]);

  const requestOtp = async ({ email, roleHint = "manager" }) => {
    setIsLoading(true);
    setPendingEmail(email);
    setAuthError("");
    try {
      if (hasSupabaseConfig && supabase) {
        const { data, error } = await supabase.functions.invoke("request-otp", {
          body: buildOtpRequestPayload(email),
        });

        if (error || data?.ok === false) {
          let edgeErrorMessage = data?.error;
          if (!edgeErrorMessage && typeof Response !== "undefined" && error?.context instanceof Response) {
            try {
              const cloned = error.context.clone();
              const body = await cloned.json();
              edgeErrorMessage = body?.error || body?.message;
            } catch (e) {
              // ignore parse failure
            }
          }
          throw normalizeOtpError(new Error(edgeErrorMessage || (error instanceof Error ? error.message : String(error)) || PROFILE_LOAD_ERROR));
        }
      } else {
        localStorage.setItem("construction-auth-role-hint", roleHint || "manager");
      }

      setIsOtpSent(true);
      return { success: true };
    } catch (error) {
      const normalizedError = normalizeOtpError(error);
      setAuthError(normalizedError.message);
      logError(error, { component: "AuthContext.sendOtp" });
      return { success: false, error: normalizedError };
    } finally {
      setIsLoading(false);
    }
  };

  const verifyOtp = async ({ email, otp }) => {
    setIsLoading(true);
    try {
      if (hasSupabaseConfig && supabase) {
        const { data, error } = await supabase.functions.invoke("verify-otp", {
          body: { email, otp },
        });

        if (error || data?.ok === false) {
          let edgeErrorMessage = data?.error;
          if (!edgeErrorMessage && typeof Response !== "undefined" && error?.context instanceof Response) {
            try {
              const cloned = error.context.clone();
              const body = await cloned.json();
              edgeErrorMessage = body?.error || body?.message;
            } catch (e) {
              // ignore parse failure
            }
          }
          throw normalizeOtpError(new Error(edgeErrorMessage || (error instanceof Error ? error.message : String(error)) || PROFILE_LOAD_ERROR));
        }

        // Clear signedOut flag — user is logging in
        signedOutRef.current = false;
        sessionStorage.removeItem(SIGNED_OUT_FLAG);

        if (data?.session?.access_token && data?.session?.refresh_token) {
          const { data: sessionData, error: sessionError } = await supabase.auth.setSession({
            access_token: data.session.access_token,
            refresh_token: data.session.refresh_token,
          });

          if (sessionError) {
            throw normalizeOtpError(sessionError);
          }

          const baseUser = mapSessionUserToAuthUser(sessionData.session?.user || data.session.user);
          if (baseUser) {
            const profile = await fetchUserProfile(baseUser.id);
            if (profile) {
              setUser(mapProfileToAuthUser(profile));
            } else {
              setUser({ ...baseUser, role: baseUser.role || "manager" });
            }
          }
        } else {
          setUser(mapSessionUserToAuthUser(data?.user || null));
        }
        setAuthError("");
        return { success: Boolean(data?.session || data?.user) };
      }

      if (otp !== "000000") {
        throw new Error("Неверный код подтверждения");
      }

      const roleHint = localStorage.getItem("construction-auth-role-hint");
      const demoUser = resolveDemoUser(email, roleHint);
      setUser(demoUser);
      return { success: true };
    } catch (error) {
      const normalizedError = normalizeOtpError(error);
      setAuthError(normalizedError.message);
      logError(error, { component: "AuthContext.verifyOtp" });
      return { success: false, error: normalizedError };
    } finally {
      setIsLoading(false);
    }
  };

  const signOut = async () => {
    // Set flag BEFORE signOut to prevent onAuthStateChange/getSession from restoring session
    signedOutRef.current = true;

    if (hasSupabaseConfig && supabase) {
      try {
        await supabase.auth.signOut({ scope: "local" });
      } catch (e) {
        // Ignore — session may already be invalid
      }
    }

    // Hard clear all auth storage so auto-login is impossible
    clearAllAuthStorage();

    setUser(null);
    setPendingEmail("");
    setIsOtpSent(false);
    setAuthError("");
  };

  const isDemoMode = !hasSupabaseConfig && import.meta.env.VITE_ENABLE_DEMO === "true";

  const value = {
    user,
    pendingEmail,
    isOtpSent,
    isLoading,
    isSessionLoading,
    authError,
    isDemoMode,
    requestOtp,
    verifyOtp,
    signOut,
    loginAsDemoUser: (demoUser) => {
      setUser(demoUser);
      setAuthError("");
    },
  };

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

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