import { useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'

import {
  Dialog,
  DialogContent,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@evobgp/ui/components/dialog'
import { Input } from '@evobgp/ui/components/input'
import { Label } from '@evobgp/ui/components/label'

import { SelectField } from '@/components/select-field'
import { Button } from '@evobgp/ui/components/button'

import { LoadingButton } from '@/components/loading-button'
import { CommunitySelect } from '@/components/modules/community-select'
import { ApiError, apiMutate } from '@/lib/api-client'
import { normalizeCdnSourceKind } from '@/lib/modules/helpers'
import type { BgpCommunity, CdnPreviewResponse, CdnSource, CdnSourceCreate } from '@/types/api'

const CDN_KIND_ITEMS = [
  { value: 'plaintext', label: 'plaintext' },
  { value: 'json', label: 'json' },
] as const

interface ModuleCdnSourceDialogProps {
  open: boolean
  moduleId: string
  edit: CdnSource | null
  communities: BgpCommunity[]
  onOpenChange: (open: boolean) => void
  onSaved: () => void | Promise<void>
}

type CdnForm = CdnSourceCreate & { refresh_interval_sec?: number | null }

export function ModuleCdnSourceDialog({
  open,
  moduleId,
  edit,
  communities,
  onOpenChange,
  onSaved,
}: ModuleCdnSourceDialogProps) {
  const [saving, setSaving] = useState(false)
  const [previewLoading, setPreviewLoading] = useState(false)
  const [previewItems, setPreviewItems] = useState<string[]>([])
  const [previewTotal, setPreviewTotal] = useState(0)
  const [previewTruncated, setPreviewTruncated] = useState(false)
  const [previewError, setPreviewError] = useState<string | null>(null)
  const [previewOk, setPreviewOk] = useState(false)
  const [form, setForm] = useState<CdnForm>({
    url: '',
    source_kind: 'plaintext',
    prefix_path: '',
    community_id: null,
  })

  const kindItems = useMemo(() => [...CDN_KIND_ITEMS], [])

  function clearPreview() {
    setPreviewLoading(false)
    setPreviewItems([])
    setPreviewTotal(0)
    setPreviewTruncated(false)
    setPreviewError(null)
    setPreviewOk(false)
  }

  useEffect(() => {
    if (!open) {
      clearPreview()
      return
    }
    setForm(
      edit
        ? {
            url: edit.url,
            source_kind: normalizeCdnSourceKind(edit.source_kind),
            prefix_path: edit.prefix_path ?? '',
            community_id: edit.community_id,
            refresh_interval_sec: edit.refresh_interval_sec,
          }
        : { url: '', source_kind: 'plaintext', prefix_path: '', community_id: null },
    )
    clearPreview()
  }, [open, edit])

  async function previewCdn() {
    const urlTrim = form.url.trim()
    if (!urlTrim) {
      toast.error('Укажите URL')
      return
    }
    setPreviewLoading(true)
    setPreviewError(null)
    setPreviewOk(false)
    try {
      const res = await apiMutate<CdnPreviewResponse>(
        `/v1/modules/${moduleId}/cdn-sources/preview`,
        'POST',
        {
          url: urlTrim,
          source_kind: form.source_kind,
          prefix_path: form.prefix_path?.trim() ?? '',
        },
      )
      setPreviewItems(res.items)
      setPreviewTotal(res.total)
      setPreviewTruncated(res.truncated)
      setPreviewOk(true)
    } catch (e) {
      setPreviewError(e instanceof ApiError ? e.message : String(e))
      setPreviewItems([])
      setPreviewTotal(0)
      setPreviewTruncated(false)
      setPreviewOk(false)
    } finally {
      setPreviewLoading(false)
    }
  }

  async function save() {
    const urlTrim = form.url.trim()
    if (!urlTrim) {
      toast.error('Укажите URL')
      return
    }
    setSaving(true)
    try {
      const body = {
        ...form,
        url: urlTrim,
        source_kind: form.source_kind,
        prefix_path: form.prefix_path?.trim() ?? '',
      }
      if (edit) {
        await apiMutate(`/v1/modules/${moduleId}/cdn-sources/${edit.id}`, 'PATCH', body)
        toast.success('Источник обновлён')
      } else {
        await apiMutate(`/v1/modules/${moduleId}/cdn-sources`, 'POST', body)
        toast.success('Источник добавлен')
      }
      clearPreview()
      onOpenChange(false)
      await onSaved()
    } catch (e) {
      toast.error(e instanceof ApiError ? e.message : String(e))
    } finally {
      setSaving(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-lg">
        <DialogHeader>
          <DialogTitle>{edit ? 'Редактировать источник' : 'Новый CDN-источник'}</DialogTitle>
        </DialogHeader>
        <div className="space-y-4 py-2">
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="cdn-url">URL</Label>
            <Input
              id="cdn-url"
              placeholder="https://example.com/list.txt"
              value={form.url}
              onChange={(e) => setForm((s) => ({ ...s, url: e.target.value }))}
            />
          </div>
          <SelectField
            id="cdn-kind"
            label="Тип источника"
            items={kindItems}
            value={form.source_kind}
            onValueChange={(v) => v && setForm((s) => ({ ...s, source_kind: v }))}
          />
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="cdn-prefix-path">JSON path (prefix_path)</Label>
            <Input
              id="cdn-prefix-path"
              placeholder="напр. prefixes[] или data.items[].cidr"
              value={form.prefix_path ?? ''}
              onChange={(e) => setForm((s) => ({ ...s, prefix_path: e.target.value }))}
            />
            {form.source_kind === 'json' && !form.prefix_path?.trim() ? (
              <p className="text-xs text-muted-foreground">
                Для JSON укажите путь к полям с CIDR; пустой путь может не дать префиксов.
              </p>
            ) : null}
          </div>
          <CommunitySelect
            id="cdn-comm"
            label="Community"
            value={form.community_id ?? null}
            onValueChange={(v) => setForm((s) => ({ ...s, community_id: v }))}
            communities={communities}
            nullable
          />
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="cdn-interval">Интервал обновления (сек)</Label>
            <Input
              id="cdn-interval"
              type="number"
              placeholder="3600"
              value={form.refresh_interval_sec ?? ''}
              onChange={(e) =>
                setForm((s) => ({
                  ...s,
                  refresh_interval_sec: e.target.value ? Number(e.target.value) : null,
                }))
              }
            />
          </div>
          <div className="flex flex-col gap-2 rounded-lg border border-border p-3">
            <div className="flex flex-wrap items-center gap-2">
              <Button
                type="button"
                variant="secondary"
                size="sm"
                onClick={() => void previewCdn()}
                disabled={previewLoading}
              >
                {previewLoading ? 'Загрузка…' : 'Предпросмотр'}
              </Button>
              {previewError ? (
                <span className="text-sm text-destructive">{previewError}</span>
              ) : previewOk ? (
                <span className="text-sm text-muted-foreground">
                  Всего: {previewTotal}
                  {previewTruncated ? (
                    <span className="text-amber-600 dark:text-amber-500"> (обрезано)</span>
                  ) : null}
                </span>
              ) : null}
            </div>
            {previewItems.length > 0 ? (
              <ul className="max-h-48 overflow-y-auto rounded-md border bg-muted/40 p-2 font-mono text-xs">
                {previewItems.map((item, i) => (
                  <li key={`${i}-${item}`} className="py-0.5">
                    {item}
                  </li>
                ))}
              </ul>
            ) : null}
          </div>
        </div>
        <DialogFooter>
          <LoadingButton variant="outline" onClick={() => onOpenChange(false)}>
            Отмена
          </LoadingButton>
          <LoadingButton loading={saving} onClick={() => void save()}>
            {edit ? 'Сохранить' : 'Добавить'}
          </LoadingButton>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
