import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from "react";

import { LoadingScreen } from "@/components/LoadingScreen";
import {
  authStoragePrefix,
  buildAccountId,
  loadAccounts,
  saveAccounts,
  saveActiveAccountId,
  saveDraftInstanceUrl,
  type SavedAccount,
} from "@/lib/accounts";
import { setRuntimeApiUrl, getApiUrl, DEFAULT_API_URL, invalidServerUrlMessage } from "@/lib/config";
import { clearAuthStorage, readStoredSessionUser } from "@/lib/auth-storage";
import { normalizeInstanceUrl, saveStoredInstanceUrl } from "@/lib/instance-url";
import { migrateStoredOfficialUrls } from "@/lib/official-url-migration";
import { clearTimeClockPrefsForAccount } from "@/lib/time-clock-prefs";

export type RemoveAccountResult = {
  wasActive: boolean;
  remainingCount: number;
};

type AccountsContextValue = {
  accounts: SavedAccount[];
  activeAccount: SavedAccount | null;
  activeAccountId: string | null;
  apiUrl: string;
  authStoragePrefix: string;
  setInstanceUrl: (url: string) => Promise<string>;
  switchAccount: (accountId: string) => Promise<void>;
  registerAccount: (input: {
    instanceUrl: string;
    userId: string;
    email: string;
    name: string;
  }) => Promise<SavedAccount>;
  removeAccount: (accountId: string) => Promise<RemoveAccountResult>;
  refreshAccounts: () => Promise<void>;
  clearActiveAccount: () => Promise<void>;
};

const AccountsContext = createContext<AccountsContextValue | null>(null);

export function AccountsProvider({ children }: { children: ReactNode }) {
  const [ready, setReady] = useState(false);
  const [accounts, setAccounts] = useState<SavedAccount[]>([]);
  const [activeAccountId, setActiveAccountId] = useState<string | null>(null);
  const [apiUrl, setApiUrl] = useState(getApiUrl);

  useEffect(() => {
    migrateStoredOfficialUrls()
      .then(({ accounts: storedAccounts, activeAccountId: activeId, draftUrl }) => {
        setAccounts(storedAccounts);

        const active = storedAccounts.find((account) => account.id === activeId) ?? null;
        if (active) {
          setActiveAccountId(active.id);
          setRuntimeApiUrl(active.instanceUrl);
        } else if (draftUrl) {
          setRuntimeApiUrl(draftUrl);
        } else if (!process.env.EXPO_PUBLIC_API_URL?.trim()) {
          setRuntimeApiUrl(DEFAULT_API_URL);
        }

        setApiUrl(getApiUrl());
      })
      .finally(() => setReady(true));
  }, []);

  const activeAccount = useMemo(
    () => accounts.find((account) => account.id === activeAccountId) ?? null,
    [accounts, activeAccountId],
  );

  const setInstanceUrl = useCallback(
    async (url: string) => {
      const normalized = normalizeInstanceUrl(url);
      if (!normalized) {
        throw new Error(invalidServerUrlMessage());
      }

      if (activeAccount) {
        const nextAccounts = accounts.map((account) =>
          account.id === activeAccount.id ? { ...account, instanceUrl: normalized } : account,
        );
        setAccounts(nextAccounts);
        await saveAccounts(nextAccounts);
      } else {
        await saveDraftInstanceUrl(normalized);
      }

      await saveStoredInstanceUrl(normalized);
      setRuntimeApiUrl(normalized);
      setApiUrl(normalized);
      return normalized;
    },
    [activeAccount, accounts],
  );

  const switchAccount = useCallback(
    async (accountId: string) => {
      const account = accounts.find((entry) => entry.id === accountId);
      if (!account) return;

      const nextAccounts = accounts.map((entry) =>
        entry.id === accountId ? { ...entry, lastUsedAt: Date.now() } : entry,
      );
      setAccounts(nextAccounts);
      await saveAccounts(nextAccounts);
      await saveActiveAccountId(accountId);
      setActiveAccountId(accountId);
      setRuntimeApiUrl(account.instanceUrl);
      setApiUrl(account.instanceUrl);
    },
    [accounts],
  );

  const registerAccount = useCallback(
    async (input: { instanceUrl: string; userId: string; email: string; name: string }) => {
      const id = buildAccountId(input.instanceUrl, input.userId);
      const existing = accounts.find((account) => account.id === id);
      const account: SavedAccount = {
        id,
        instanceUrl: input.instanceUrl,
        userId: input.userId,
        email: input.email,
        name: input.name,
        lastUsedAt: Date.now(),
      };

      const nextAccounts = existing
        ? accounts.map((entry) => (entry.id === id ? account : entry))
        : [account, ...accounts.filter((entry) => entry.id !== id)];

      setAccounts(nextAccounts);
      await saveAccounts(nextAccounts);
      await saveActiveAccountId(id);
      await saveDraftInstanceUrl(null);
      setActiveAccountId(id);
      setRuntimeApiUrl(input.instanceUrl);
      setApiUrl(input.instanceUrl);
      await saveStoredInstanceUrl(input.instanceUrl);
      return account;
    },
    [accounts],
  );

  const removeAccount = useCallback(
    async (accountId: string): Promise<RemoveAccountResult> => {
      const wasActive = activeAccountId === accountId;

      await clearAuthStorage(authStoragePrefix(accountId));
      await clearTimeClockPrefsForAccount(accountId);

      const nextAccounts = accounts.filter((account) => account.id !== accountId);
      setAccounts(nextAccounts);
      await saveAccounts(nextAccounts);

      if (wasActive) {
        await saveActiveAccountId(null);
        setActiveAccountId(null);
      }

      return { wasActive, remainingCount: nextAccounts.length };
    },
    [accounts, activeAccountId],
  );

  const refreshAccounts = useCallback(async () => {
    const stored = await loadAccounts();
    const refreshed = await Promise.all(
      stored.map(async (account) => {
        const user = await readStoredSessionUser(authStoragePrefix(account.id));
        if (!user?.name && !user?.email) return account;
        return {
          ...account,
          name: user.name?.trim() || account.name,
          email: user.email?.trim() || account.email,
        };
      }),
    );

    setAccounts(refreshed);
    await saveAccounts(refreshed);
  }, []);

  const clearActiveAccount = useCallback(async () => {
    await saveActiveAccountId(null);
    setActiveAccountId(null);
  }, []);

  const value = useMemo(
    () => ({
      accounts,
      activeAccount,
      activeAccountId,
      apiUrl,
      authStoragePrefix: activeAccount
        ? authStoragePrefix(activeAccount.id)
        : "beenvoice:guest",
      setInstanceUrl,
      switchAccount,
      registerAccount,
      removeAccount,
      refreshAccounts,
      clearActiveAccount,
    }),
    [
      accounts,
      activeAccount,
      activeAccountId,
      apiUrl,
      setInstanceUrl,
      switchAccount,
      registerAccount,
      removeAccount,
      refreshAccounts,
      clearActiveAccount,
    ],
  );

  if (!ready) {
    return <LoadingScreen message="Starting…" />;
  }

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

export function useAccounts() {
  const ctx = useContext(AccountsContext);
  if (!ctx) throw new Error("useAccounts must be used within AccountsProvider");
  return ctx;
}
