import AsyncStorage from "@react-native-async-storage/async-storage";
import * as SecureStore from "expo-secure-store";
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
import { Platform } from "react-native";

import { authenticate, loadCatalog, normalizeServerUrl, XtreamApiError } from "./api";
import {
  emptyCatalog,
  type FavoriteKey,
  type XtreamAccount,
  type XtreamCatalog,
  type XtreamCredentials,
} from "./types";

const CREDENTIALS_KEY = "manus.iptv.credentials";
const FAVORITES_KEY = "manus.iptv.favorites";

type IptvContextValue = {
  credentials: XtreamCredentials | null;
  account: XtreamAccount | null;
  catalog: XtreamCatalog;
  favorites: FavoriteKey[];
  isBootstrapping: boolean;
  isLoading: boolean;
  error: string | null;
  rememberLogin: boolean;
  setRememberLogin: (value: boolean) => void;
  connect: (credentials: XtreamCredentials) => Promise<void>;
  reloadCatalog: () => Promise<void>;
  logout: () => Promise<void>;
  toggleFavorite: (key: FavoriteKey) => void;
  isFavorite: (key: FavoriteKey) => boolean;
  clearError: () => void;
};

const IptvContext = createContext<IptvContextValue | null>(null);

async function setSecureValue(key: string, value: string): Promise<void> {
  if (Platform.OS === "web") {
    localStorage.setItem(key, value);
    return;
  }
  await SecureStore.setItemAsync(key, value);
}

async function getSecureValue(key: string): Promise<string | null> {
  if (Platform.OS === "web") return localStorage.getItem(key);
  return SecureStore.getItemAsync(key);
}

async function deleteSecureValue(key: string): Promise<void> {
  if (Platform.OS === "web") {
    localStorage.removeItem(key);
    return;
  }
  await SecureStore.deleteItemAsync(key);
}

export function IptvProvider({ children }: { children: React.ReactNode }) {
  const [credentials, setCredentials] = useState<XtreamCredentials | null>(null);
  const [account, setAccount] = useState<XtreamAccount | null>(null);
  const [catalog, setCatalog] = useState<XtreamCatalog>(emptyCatalog);
  const [favorites, setFavorites] = useState<FavoriteKey[]>([]);
  const [rememberLogin, setRememberLogin] = useState(true);
  const [isBootstrapping, setIsBootstrapping] = useState(Platform.OS !== "web");
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let active = true;
    (async () => {
      try {
        const [savedCredentials, savedFavorites] = await Promise.all([
          getSecureValue(CREDENTIALS_KEY),
          AsyncStorage.getItem(FAVORITES_KEY),
        ]);
        if (!active) return;
        if (savedFavorites) {
          try {
            const parsed = JSON.parse(savedFavorites);
            if (Array.isArray(parsed)) setFavorites(parsed as FavoriteKey[]);
          } catch {
            await AsyncStorage.removeItem(FAVORITES_KEY);
          }
        }
        if (savedCredentials) {
          try {
            const parsed = JSON.parse(savedCredentials) as XtreamCredentials;
            if (parsed.serverUrl && parsed.username && parsed.password) {
              setRememberLogin(true);
              await connectInternal(parsed, true);
            }
          } catch (storedError) {
            if (active && storedError instanceof XtreamApiError && storedError.code === "auth") {
              await deleteSecureValue(CREDENTIALS_KEY);
            }
          }
        }
      } finally {
        if (active) setIsBootstrapping(false);
      }
    })();
    return () => {
      active = false;
    };
  }, []);

  const connectInternal = useCallback(
    async (input: XtreamCredentials, silent = false) => {
      if (!silent) setError(null);
      setIsLoading(true);
      try {
        const normalized: XtreamCredentials = {
          serverUrl: normalizeServerUrl(input.serverUrl),
          username: input.username.trim(),
          password: input.password,
        };
        const accountResult = await authenticate(normalized);
        setCredentials(normalized);
        setAccount(accountResult);
        if (rememberLogin) {
          await setSecureValue(CREDENTIALS_KEY, JSON.stringify(normalized));
        }
        try {
          const catalogResult = await loadCatalog(normalized);
          setCatalog(catalogResult);
        } catch (catalogError) {
          setCatalog(emptyCatalog);
          if (!silent) {
            setError(
              catalogError instanceof Error
                ? `${catalogError.message} A conta foi conectada, mas o catálogo não carregou.`
                : "A conta foi conectada, mas o catálogo não carregou.",
            );
          }
        }
      } catch (connectError) {
        if (!silent) {
          setError(
            connectError instanceof Error
              ? connectError.message
              : "Não foi possível conectar à conta.",
          );
        }
        throw connectError;
      } finally {
        setIsLoading(false);
      }
    },
    [rememberLogin],
  );

  const connect = useCallback(
    async (input: XtreamCredentials) => {
      await connectInternal(input);
    },
    [connectInternal],
  );

  const reloadCatalog = useCallback(async () => {
    if (!credentials) return;
    setError(null);
    setIsLoading(true);
    try {
      setCatalog(await loadCatalog(credentials));
    } catch (reloadError) {
      setError(reloadError instanceof Error ? reloadError.message : "Não foi possível atualizar o catálogo.");
    } finally {
      setIsLoading(false);
    }
  }, [credentials]);

  const logout = useCallback(async () => {
    await deleteSecureValue(CREDENTIALS_KEY);
    setCredentials(null);
    setAccount(null);
    setCatalog(emptyCatalog);
    setError(null);
  }, []);

  const toggleFavorite = useCallback((key: FavoriteKey) => {
    setFavorites((current) => {
      const next = current.includes(key) ? current.filter((item) => item !== key) : [...current, key];
      void AsyncStorage.setItem(FAVORITES_KEY, JSON.stringify(next));
      return next;
    });
  }, []);

  const isFavorite = useCallback((key: FavoriteKey) => favorites.includes(key), [favorites]);
  const clearError = useCallback(() => setError(null), []);

  const value = useMemo<IptvContextValue>(
    () => ({
      credentials,
      account,
      catalog,
      favorites,
      isBootstrapping,
      isLoading,
      error,
      rememberLogin,
      setRememberLogin,
      connect,
      reloadCatalog,
      logout,
      toggleFavorite,
      isFavorite,
      clearError,
    }),
    [
      credentials,
      account,
      catalog,
      favorites,
      isBootstrapping,
      isLoading,
      error,
      rememberLogin,
      connect,
      reloadCatalog,
      logout,
      toggleFavorite,
      isFavorite,
      clearError,
    ],
  );

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

export function useIptv() {
  const context = useContext(IptvContext);
  if (!context) throw new Error("useIptv must be used inside IptvProvider");
  return context;
}
