"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"

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)

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 res = await fetch(`${backendUrl.replace(/\/$/, "")}/api/evobgp/catalog`, {
          method: "POST",
        })
        const text = await res.text()
        if (!res.ok) {
          let msg = res.statusText
          try {
            const j = JSON.parse(text) as { error?: string; detail?: string }
            msg = j.error ?? j.detail ?? msg
          } catch {
            if (text) msg = text
          }
          throw new Error(msg || "Ошибка EvoBGP")
        }
        setSnapshot(JSON.parse(text) as EvoBgpCatalogSnapshot)
      } catch (e) {
        setSnapshot(null)
        setError(e instanceof Error ? e.message : "Ошибка загрузки")
      } finally {
        setLoading(false)
      }
    },
    [mode, backendStatus, backendUrl],
  )

  const loadSettings = useCallback(async () => {
    if (mode !== "live" || backendStatus !== true) {
      setSettingsLoaded(false)
      setSnapshot(null)
      setError(null)
      return
    }
    try {
      const res = await fetch(`${backendUrl.replace(/\/$/, "")}/api/evobgp/settings`)
      if (!res.ok) throw new Error(await res.text())
      const data = (await res.json()) as EvoBgpSettingsDto
      setBaseUrlState(data.baseUrl ?? "")
      setEnabledState(data.enabled ?? false)
      setSecretConfigured(data.secretConfigured ?? false)
      setSettingsLoaded(true)
      await pullCatalog(data.enabled ?? false)
    } catch {
      setSettingsLoaded(true)
    }
  }, [mode, backendStatus, backendUrl, pullCatalog])

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

  const saveSettings = useCallback(
    async (patch: EvoBgpSavePayload) => {
      const res = await fetch(`${backendUrl.replace(/\/$/, "")}/api/evobgp/settings`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(patch),
      })
      const text = await res.text()
      if (!res.ok) {
        let msg = res.statusText
        try {
          const j = JSON.parse(text) as { error?: string }
          msg = j.error ?? msg
        } catch {
          if (text) msg = text
        }
        throw new Error(msg || "Не удалось сохранить")
      }
      const data = JSON.parse(text) as EvoBgpSettingsDto
      setBaseUrlState(data.baseUrl ?? "")
      setEnabledState(data.enabled ?? false)
      setSecretConfigured(data.secretConfigured ?? false)
      await pullCatalog(data.enabled ?? false)
    },
    [backendUrl, pullCatalog],
  )

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

  const testConnection = useCallback(async (draft?: EvoBgpTestDraft) => {
    try {
      const res = await fetch(`${backendUrl.replace(/\/$/, "")}/api/evobgp/test`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(draft ?? {}),
      })
      const data = (await res.json().catch(() => ({}))) as { ok?: boolean; error?: string }
      if (!res.ok) throw new Error(data.error ?? res.statusText)
      return { ok: true, message: "Соединение с EvoBGP установлено" }
    } catch (e) {
      return { ok: false, message: e instanceof Error ? e.message : "Ошибка" }
    }
  }, [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
}
