import { createContext, useContext, createSignal, createMemo, onMount, JSXElement } from "solid-js";
import html from "solid-js/html";
import { getToken, setToken, clearToken, isTokenExpired } from "./useAuthFetch.js";
import { apiFetch } from "./checkBundleVersion.js";

export interface Identity {
    id: string;
    username: string;
    email: string;
    first_name: string;
    last_name: string;
    permissions: string[];
    org_id: string | null;
    org_name: string | null;
    org_cert_num: string | null;
}

export interface MemberOrg {
    id: string;
    name: string;
    certnum: string | null;
}

export interface SuperAdminState {
    enabled: boolean;
    orgName: string | null;
    firstMemberOrgId: string | null;
}

export interface LoginResult {
    success: boolean;
    error?: string;
}

export interface SwitchOrgResult {
    success: boolean;
    org_id?: string;
    org_name?: string;
    org_cert_num?: string | null;
    error?: string;
}

export interface AuthContextValue {
    identity: () => Identity | null;
    loading: () => boolean;
    error: () => string | null;
    isAuthenticated: () => boolean;
    superAdminMode: () => SuperAdminState;
    memberOrganizations: () => MemberOrg[];
    login: (username: string, password: string, rememberMe?: boolean) => Promise<LoginResult>;
    logout: () => Promise<void>;
    checkAuth: () => Promise<void>;
    switchOrganization: (orgId: string) => Promise<SwitchOrgResult>;
    disableSuperAdminMode: () => Promise<void>;
}

interface AuthProviderProps {
    children?: JSXElement;
}

const AuthContext = createContext<AuthContextValue>();

export function AuthProvider(props: AuthProviderProps) {
    const [identity, setIdentity] = createSignal<Identity | null>(null);
    const [loading, setLoading] = createSignal(true);
    const [error, setError] = createSignal<string | null>(null);
    const [memberOrganizations, setMemberOrganizations] = createSignal<MemberOrg[]>([]);
    const [isAdmin, setIsAdmin] = createSignal(false);

    const isAuthenticated = createMemo(() => !!identity());

    const checkAuth = async (): Promise<void> => {
        try {
            setLoading(true);
            setError(null);

            const token = getToken();

            if (!token) {
                setIdentity(null);
                setLoading(false);
                return;
            }

            if (isTokenExpired()) {
                clearToken();
                setIdentity(null);
                setLoading(false);
                return;
            }

            // Retry transient network failures a few times so a brief blip
            // self-heals instead of dropping the user to an error screen. A
            // 401 is a real response and is handled below, not retried. -mta 6/3/26
            let response;
            for (let attempt = 0; ; attempt++) {
                try {
                    response = await apiFetch("/api/auth/me", {
                        headers: {
                            "Authorization": `Bearer ${token}`,
                        },
                    });
                    break;
                } catch (err) {
                    if (attempt >= 2) throw err;
                    await new Promise((resolve) => setTimeout(resolve, 400 * (attempt + 1)));
                }
            }

            // Only a definitive 401 means the session is actually invalid. A
            // network error, an aborted request (e.g. the user refreshed
            // mid-flight), or a 5xx must NOT clear the token -- otherwise a
            // quick refresh aborts this request and silently logs the user
            // out. -mta 6/3/26
            if (response.status === 401) {
                clearToken();
                setIdentity(null);
                setMemberOrganizations([]);
                setIsAdmin(false);
                return;
            }

            if (!response.ok) {
                throw new Error(`Auth check failed: ${response.status}`);
            }

            const data = await response.json();
            setIdentity(data);

            const orgsResponse = await apiFetch("/api/auth/my-organizations", {
                headers: {
                    "Authorization": `Bearer ${token}`,
                },
            });

            if (orgsResponse.ok) {
                const orgsData = await orgsResponse.json();
                setMemberOrganizations(orgsData.member_organizations || []);
                setIsAdmin(orgsData.is_admin || false);
            }
        } catch (err) {
            // Transient failure (network/abort/5xx). Preserve the token so the
            // next load can retry instead of forcing a logout. -mta 6/3/26
            setError(err instanceof Error ? err.message : "Unknown error");
        } finally {
            setLoading(false);
        }
    };

    onMount(() => {
        checkAuth();
    });

    const login = async (username: string, password: string, rememberMe = false): Promise<LoginResult> => {
        try {
            setLoading(true);
            setError(null);

            const response = await apiFetch("/api/auth/login", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                },
                body: JSON.stringify({
                    username,
                    password,
                    remember_me: rememberMe,
                }),
            });

            const data = await response.json();

            if (!response.ok || !data.success) {
                throw new Error(data.error || "Login failed");
            }

            setToken(data.token, data.expires_at);
            setIdentity(data.user);

            const orgsResponse = await apiFetch("/api/auth/my-organizations", {
                headers: {
                    "Authorization": `Bearer ${data.token}`,
                },
            });

            if (orgsResponse.ok) {
                const orgsData = await orgsResponse.json();
                setMemberOrganizations(orgsData.member_organizations || []);
                setIsAdmin(orgsData.is_admin || false);
            }

            return { success: true };
        } catch (err) {
            const message = err instanceof Error ? err.message : "Unknown error";
            setError(message);
            return { success: false, error: message };
        } finally {
            setLoading(false);
        }
    };

    const logout = async (): Promise<void> => {
        const token = getToken();

        if (token) {
            try {
                await apiFetch("/api/auth/logout", {
                    method: "POST",
                    headers: {
                        "Authorization": `Bearer ${token}`,
                    },
                });
            } catch {
                // Continue with logout even if API call fails
            }
        }

        clearToken();
        setIdentity(null);
        window.location.href = "/login";
    };

    const switchOrganization = async (orgId: string): Promise<SwitchOrgResult> => {
        try {
            setError(null);

            const token = getToken();
            if (!token) {
                throw new Error("Not authenticated");
            }

            const response = await apiFetch("/api/auth/switch-org", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${token}`,
                },
                body: JSON.stringify({ org_id: orgId }),
            });

            const data = await response.json();

            if (!response.ok || !data.success) {
                throw new Error(data.error || "Failed to switch organization");
            }

            setIdentity((prev) => prev ? {
                ...prev,
                org_id: data.org_id,
                org_name: data.org_name,
                org_cert_num: data.org_cert_num,
            } : null);

            return {
                success: true,
                org_id: data.org_id,
                org_name: data.org_name,
                org_cert_num: data.org_cert_num,
            };
        } catch (err) {
            const message = err instanceof Error ? err.message : "Unknown error";
            setError(message);
            return { success: false, error: message };
        }
    };

    const disableSuperAdminMode = async (): Promise<void> => {
        const orgs = memberOrganizations();
        if (orgs.length > 0) {
            await switchOrganization(orgs[0].id);
        }
    };

    const superAdminMode = (): SuperAdminState => {
        const id = identity();
        const orgs = memberOrganizations();
        return {
            enabled: isAdmin() && id?.org_id != null && !orgs.some((org) => org.id === id.org_id),
            orgName: id?.org_name || null,
            firstMemberOrgId: orgs.length > 0 ? orgs[0].id : null,
        };
    };

    const value: AuthContextValue = {
        identity,
        loading,
        error,
        isAuthenticated,
        superAdminMode,
        memberOrganizations,
        login,
        logout,
        checkAuth,
        switchOrganization,
        disableSuperAdminMode,
    };

    // CRITICAL: Use lazy children evaluation
    return html`<${AuthContext.Provider} value=${value}>${() => props.children}<//>`;
}

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