"use client"

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react"
import { useDataSource } from "@/lib/data-source"
import type { Domain, IpRange, Asn } from "@/lib/data"
import { ApiClientError, requestJson } from "@/shared/api/http-client"

export interface EvoBgpCommunityRow {
  id: string
  value: string
  name: string
  description: string
  type: "standard" | "no-export" | "no-advertise" | "local-as" | "custom"
  filterIds: string[]
  serverCount: number
  prefixCount: number
  action: "permit" | "deny" | "local-pref" | "metric"
  actionValue?: number
  enabled: boolean
}

export interface EvoBgpCatalogSnapshot {
  fetchedAt: string
  modules: { id: string; name: string; type: string }[]
  domains: Domain[]
  ipRanges: IpRange[]
  asns: Asn[]
  communities: EvoBgpCommunityRow[]
}

export interface EvoBgpSettingsDto {
  baseUrl: string
  enabled: boolean
  secretConfigured: boolean
}

/** undefined — не менять ключ; null или очистка через отдельное сохранение */
export type EvoBgpSavePayload = {
  baseUrl?: string
  enabled?: boolean
  apiKey?: string | null
}

/** Черновик для POST /evobgp/test: пустой объект — всё из БД */
export type EvoBgpTestDraft = { baseUrl?: string; apiKey?: string }

interface EvoBgpContextValue {
  baseUrl: string
  enabled: boolean
  secretConfigured: boolean
  settingsLoaded: boolean
  snapshot: EvoBgpCatalogSnapshot | null
  loading: boolean
  error: string | null
  loadSettings: () => Promise<void>
  saveSettings: (patch: EvoBgpSavePayload) => Promise<void>
  refresh: () => Promise<void>
  testConnection: (draft?: EvoBgpTestDraft) => Promise<{ ok: boolean; message: string }>
}

const EvoBgpContext = createContext<EvoBgpContextValue | null>(null)

function errorMessage(e: unknown, fallback: string): string {
  if (e instanceof ApiClientError) return e.message || fallback
  if (e instanceof Error) return e.message || fallback
  return fallback
}

export function EvoBGPProvider({ children }: { children: React.ReactNode }) {
  const { mode, backendUrl, backendStatus } = useDataSource()
  const [baseUrl, setBaseUrlState] = useState("")
  const [enabled, setEnabledState] = useState(false)
  const [secretConfigured, setSecretConfigured] = useState(false)
  const [settingsLoaded, setSettingsLoaded] = useState(false)
  const [snapshot, setSnapshot] = useState<EvoBgpCatalogSnapshot | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const pullCatalog = useCallback(
    async (catalogEnabled: boolean) => {
      if (mode !== "live" || backendStatus !== true || !catalogEnabled) {
        setSnapshot(null)
        setError(null)
        return
      }
      setLoading(true)
      setError(null)
      try {
        const data = await requestJson<EvoBgpCatalogSnapshot>(
          backendUrl,
          "/api/evobgp/catalog",
          { method: "POST" },
        )
        setSnapshot(data)
      } catch (e) {
        setSnapshot(null)
        setError(errorMessage(e, "Ошибка загрузки"))
      } finally {
        setLoading(false)
      }
    },
    [mode, backendStatus, backendUrl],
  )

  const loadSettings = useCallback(async () => {
    if (mode !== "live" || backendStatus !== true) {
      setSettingsLoaded(false)
      setSnapshot(null)
      setError(null)
      return
    }
    try {
      const data = await requestJson<EvoBgpSettingsDto>(
        backendUrl,
        "/api/evobgp/settings",
      )
      setBaseUrlState(data.baseUrl ?? "")
      setEnabledState(Boolean(data.enabled))
      setSecretConfigured(Boolean(data.secretConfigured))
      setSettingsLoaded(true)
      setError(null)
      await pullCatalog(Boolean(data.enabled))
    } catch (e) {
      setSettingsLoaded(true)
      setError(errorMessage(e, "Не удалось загрузить настройки EvoBGP"))
    }
  }, [mode, backendStatus, backendUrl, pullCatalog])

  useEffect(() => {
    queueMicrotask(() => {
      void loadSettings()
    })
  }, [loadSettings])

  const saveSettings = useCallback(
    async (patch: EvoBgpSavePayload) => {
      const data = await requestJson<EvoBgpSettingsDto>(
        backendUrl,
        "/api/evobgp/settings",
        {
          method: "PUT",
          body: JSON.stringify(patch),
        },
      )
      const nextEnabled = Boolean(data.enabled)
      setBaseUrlState(data.baseUrl ?? "")
      setEnabledState(nextEnabled)
      setSecretConfigured(Boolean(data.secretConfigured))
      setError(null)
      // Каталог не должен ронять успех сохранения (401/502 на catalog ≠ «настройки не сохранились»)
      try {
        await pullCatalog(nextEnabled)
      } catch {
        /* pullCatalog already sets error state */
      }
    },
    [backendUrl, pullCatalog],
  )

  const refresh = useCallback(async () => {
    await pullCatalog(enabled)
  }, [enabled, pullCatalog])

  const testConnection = useCallback(
    async (draft?: EvoBgpTestDraft) => {
      try {
        await requestJson<{ ok?: boolean }>(backendUrl, "/api/evobgp/test", {
          method: "POST",
          body: JSON.stringify(draft ?? {}),
        })
        return { ok: true, message: "Соединение с EvoBGP установлено" }
      } catch (e) {
        return { ok: false, message: errorMessage(e, "Ошибка") }
      }
    },
    [backendUrl],
  )

  const value = useMemo(
    () => ({
      baseUrl,
      enabled,
      secretConfigured,
      settingsLoaded,
      snapshot,
      loading,
      error,
      loadSettings,
      saveSettings,
      refresh,
      testConnection,
    }),
    [
      baseUrl,
      enabled,
      secretConfigured,
      settingsLoaded,
      snapshot,
      loading,
      error,
      loadSettings,
      saveSettings,
      refresh,
      testConnection,
    ],
  )

  return <EvoBgpContext.Provider value={value}>{children}</EvoBgpContext.Provider>
}

export function useEvoBGP(): EvoBgpContextValue {
  const ctx = useContext(EvoBgpContext)
  if (!ctx) throw new Error("useEvoBGP must be used within EvoBGPProvider")
  return ctx
}
