import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";

import { api } from "@/src/api";
import { storage } from "@/src/utils/storage";
import { nativeVpn, NativeVpnStatus } from "@/src/vpn/native";

export type VpnServer = { id: string; name: string; country: string; protocol: "wireguard" | "openvpn"; endpoint?: string };
export type VpnStatus = NativeVpnStatus | "reconnecting" | "unavailable";
export type VpnPrefs = { autoReconnect: boolean; killSwitch: boolean; dnsLeakProtection: boolean };

type Ctx = {
  available: boolean;
  status: VpnStatus;
  error: string | null;
  serverId: string | null;
  prefs: VpnPrefs;
  selectServer: (id: string) => void;
  setPref: (k: keyof VpnPrefs, v: boolean) => void;
  connect: () => Promise<void>;
  disconnect: () => Promise<void>;
};

const VpnCtx = createContext<Ctx>(null as unknown as Ctx);
const MAX_RETRIES = 5;

export function VpnProvider({ children }: { children: React.ReactNode }) {
  const available = !!nativeVpn;
  const [status, setStatus] = useState<VpnStatus>(available ? "disconnected" : "unavailable");
  const [error, setError] = useState<string | null>(null);
  const [serverId, setServerId] = useState<string | null>(null);
  const [prefs, setPrefs] = useState<VpnPrefs>({ autoReconnect: true, killSwitch: true, dnsLeakProtection: true });
  const wantConnected = useRef(false);
  const retries = useRef(0);

  useEffect(() => {
    storage.getItem<string | null>("vpn_server", null).then((v) => v && setServerId(v));
    storage.getItem<string | null>("vpn_prefs", null).then((v) => v && setPrefs(JSON.parse(v)));
  }, []);

  const connect = useCallback(async () => {
    setError(null);
    if (!nativeVpn) {
      setError("O módulo VPN nativo não está presente neste build (Expo Go/Web). Gere um build Android para usar a VPN real.");
      return;
    }
    if (!serverId) {
      setError("Selecione um servidor.");
      return;
    }
    wantConnected.current = true;
    setStatus("connecting");
    try {
      const srv = await api<{ protocol: "wireguard" | "openvpn"; config: string }>(`/vpn/servers/${serverId}/config`);
      const settings = await api<{ dns_servers?: string[] }>("/vpn/settings");
      if (!(await nativeVpn.prepare())) throw new Error("Permissão de VPN negada pelo usuário.");
      await nativeVpn.connect({
        protocol: srv.protocol,
        config: srv.config,
        dnsServers: settings.dns_servers ?? [],
        killSwitch: prefs.killSwitch,
        dnsLeakProtection: prefs.dnsLeakProtection,
      });
    } catch (e: any) {
      wantConnected.current = false;
      setStatus("error");
      setError(e?.message ?? "Falha ao conectar");
    }
  }, [serverId, prefs]);

  const disconnect = useCallback(async () => {
    wantConnected.current = false;
    retries.current = 0;
    await nativeVpn?.disconnect();
    setStatus(available ? "disconnected" : "unavailable");
  }, [available]);

  // Status events from the native tunnel + automatic reconnection with backoff.
  useEffect(() => {
    if (!nativeVpn) return;
    const sub = nativeVpn.addListener("onStatusChange", (e) => {
      if (e.status === "connected") retries.current = 0;
      const dropped = (e.status === "disconnected" || e.status === "error") && wantConnected.current;
      if (dropped && prefs.autoReconnect && retries.current < MAX_RETRIES) {
        retries.current += 1;
        setStatus("reconnecting");
        setTimeout(() => wantConnected.current && connect(), 2000 * retries.current);
        return;
      }
      setStatus(e.status);
      if (e.error) setError(e.error);
    });
    return () => sub.remove();
  }, [prefs.autoReconnect, connect]);

  const selectServer = (id: string) => {
    setServerId(id);
    storage.setItem("vpn_server", id);
  };
  const setPref = (k: keyof VpnPrefs, v: boolean) => {
    const next = { ...prefs, [k]: v };
    setPrefs(next);
    storage.setItem("vpn_prefs", JSON.stringify(next));
  };

  return (
    <VpnCtx.Provider value={{ available, status, error, serverId, prefs, selectServer, setPref, connect, disconnect }}>
      {children}
    </VpnCtx.Provider>
  );
}

export const useVpn = () => useContext(VpnCtx);
