import { useMutation } from '@tanstack/react-query'
import { Plus, Trash2 } from 'lucide-react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import type { AnnualRange, DateRange, GridLayerDto, LayerApplicability } from '@/shared/api/types'
import { Button } from '@/shared/ui/button'
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/shared/ui/dialog'
import { Input } from '@/shared/ui/input'
import { Label } from '@/shared/ui/label'
import { useKeyedList } from '@/shared/lib/keyed-list'
import { updateLayer } from '../api'
import { isEmpty, toIsoDate } from '../lib/applicability'

const WEEKDAYS = [1, 2, 3, 4, 5, 6, 0]

/**
 * Когда действует слой (см. 3.4). Разделы объединяются по ИЛИ: слой применим, если дата подходит
 * хотя бы под одно условие. Пустая применимость — слой действует всегда.
 */
export function LayerApplicabilityDialog({
  layer,
  onClose,
  onChanged,
  onError,
}: Readonly<{
  layer: GridLayerDto
  onClose: () => void
  onChanged: () => void
  onError: (error: unknown) => void
}>) {
  const { t } = useTranslation()
  const [name, setName] = useState(layer.name)
  const [weekdays, setWeekdays] = useState<number[]>(layer.applicability?.weekdays ?? [])
  const dateRanges = useKeyedList<DateRange>(layer.applicability?.dateRanges ?? [])
  const annualRanges = useKeyedList<AnnualRange>(layer.applicability?.annualRanges ?? [])
  const specificDates = useKeyedList<string>(layer.applicability?.specificDates ?? [])

  const save = useMutation({
    mutationFn: () => {
      const applicability: LayerApplicability = {
        weekdays: weekdays.length > 0 ? [...weekdays].sort((a, b) => a - b) : null,
        dateRanges: dateRanges.values.length > 0 ? dateRanges.values : null,
        annualRanges: annualRanges.values.length > 0 ? annualRanges.values : null,
        specificDates: specificDates.values.length > 0 ? specificDates.values : null,
      }
      return updateLayer(layer.id, {
        name: name.trim() || layer.name,
        priority: layer.priority,
        // Пустую применимость отправляем как null — «действует всегда» и «пустые списки» это одно
        // и то же, но null читается однозначно.
        applicability: isEmpty(applicability) ? null : applicability,
        isEnabled: layer.isEnabled,
      })
    },
    onSuccess: () => {
      onChanged()
      onClose()
    },
    onError,
  })

  const toggleWeekday = (day: number) =>
    setWeekdays((current) =>
      current.includes(day) ? current.filter((d) => d !== day) : [...current, day],
    )

  const today = toIsoDate(new Date())

  return (
    <Dialog open onOpenChange={(open) => !open && onClose()}>
      <DialogContent className="max-w-xl">
        <DialogHeader>
          <DialogTitle>{t('admin.channels.layerApplicability')}</DialogTitle>
          <DialogDescription>{t('admin.channels.applicabilityHint')}</DialogDescription>
        </DialogHeader>

        <div className="flex max-h-[60vh] flex-col gap-4 overflow-y-auto text-sm">
          <div className="flex flex-col gap-1.5">
            <Label>{t('admin.channels.layerName')}</Label>
            <Input value={name} maxLength={128} onChange={(e) => setName(e.target.value)} />
          </div>

          <div className="flex flex-col gap-1.5">
            <Label>{t('admin.channels.applicabilityWeekdays')}</Label>
            <div className="flex flex-wrap gap-1">
              {WEEKDAYS.map((day) => (
                <button
                  key={day}
                  type="button"
                  onClick={() => toggleWeekday(day)}
                  className={
                    weekdays.includes(day)
                      ? 'rounded border border-primary bg-primary/15 px-2 py-1 text-xs text-primary'
                      : 'rounded border border-border px-2 py-1 text-xs text-muted-foreground'
                  }
                >
                  {t(`admin.channels.weekdays.${day}`)}
                </button>
              ))}
            </div>
          </div>

          <Section
            title={t('admin.channels.applicabilityDateRanges')}
            onAdd={() => dateRanges.add({ from: today, to: today })}
            empty={dateRanges.rows.length === 0}
          >
            {dateRanges.rows.map(({ key, value: range }) => (
              <li key={key} className="flex flex-wrap items-end gap-2">
                <Input
                  type="date"
                  className="w-40"
                  value={range.from}
                  onChange={(e) => dateRanges.patch(key, (r) => ({ ...r, from: e.target.value }))}
                />
                <Input
                  type="date"
                  className="w-40"
                  value={range.to}
                  onChange={(e) => dateRanges.patch(key, (r) => ({ ...r, to: e.target.value }))}
                />
                <Button size="sm" variant="ghost" onClick={() => dateRanges.remove(key)}>
                  <Trash2 className="h-4 w-4" />
                </Button>
              </li>
            ))}
          </Section>

          <Section
            title={t('admin.channels.applicabilityAnnual')}
            onAdd={() => annualRanges.add({ fromMonth: 12, fromDay: 20, toMonth: 1, toDay: 8 })}
            empty={annualRanges.rows.length === 0}
          >
            {annualRanges.rows.map(({ key, value: range }) => (
              <li key={key} className="flex flex-wrap items-end gap-2">
                <MonthDay
                  value={range}
                  prefix="from"
                  onChange={(part) => annualRanges.patch(key, (r) => ({ ...r, ...part }))}
                />
                <span className="pb-2 text-muted-foreground">—</span>
                <MonthDay
                  value={range}
                  prefix="to"
                  onChange={(part) => annualRanges.patch(key, (r) => ({ ...r, ...part }))}
                />
                <Button size="sm" variant="ghost" onClick={() => annualRanges.remove(key)}>
                  <Trash2 className="h-4 w-4" />
                </Button>
              </li>
            ))}
          </Section>

          <Section
            title={t('admin.channels.applicabilityDates')}
            onAdd={() => specificDates.add(today)}
            empty={specificDates.rows.length === 0}
          >
            {specificDates.rows.map(({ key, value: date }) => (
              <li key={key} className="flex items-end gap-2">
                <Input
                  type="date"
                  className="w-40"
                  value={date}
                  onChange={(e) => specificDates.patch(key, () => e.target.value)}
                />
                <Button size="sm" variant="ghost" onClick={() => specificDates.remove(key)}>
                  <Trash2 className="h-4 w-4" />
                </Button>
              </li>
            ))}
          </Section>
        </div>

        <DialogFooter>
          <Button size="sm" variant="outline" onClick={onClose}>
            {t('common.cancel')}
          </Button>
          <Button size="sm" disabled={save.isPending} onClick={() => save.mutate()}>
            {t('common.save')}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

function Section({
  title,
  onAdd,
  empty,
  children,
}: Readonly<{
  title: string
  onAdd: () => void
  empty: boolean
  children: React.ReactNode
}>) {
  const { t } = useTranslation()
  return (
    <div className="flex flex-col gap-2">
      <div className="flex items-center justify-between gap-2">
        <Label>{title}</Label>
        <Button size="sm" variant="ghost" onClick={onAdd}>
          <Plus className="h-4 w-4" />
        </Button>
      </div>
      {empty ? (
        <p className="text-xs text-muted-foreground">{t('admin.channels.applicabilityNone')}</p>
      ) : (
        <ul className="flex flex-col gap-2">{children}</ul>
      )}
    </div>
  )
}

/** Пара «месяц / день» ежегодного периода — год у него намеренно отсутствует. */
function MonthDay({
  value,
  prefix,
  onChange,
}: Readonly<{
  value: AnnualRange
  prefix: 'from' | 'to'
  onChange: (part: Partial<AnnualRange>) => void
}>) {
  const month = prefix === 'from' ? value.fromMonth : value.toMonth
  const day = prefix === 'from' ? value.fromDay : value.toDay

  return (
    <span className="flex items-end gap-1">
      <Input
        type="number"
        min={1}
        max={12}
        className="w-16"
        value={month}
        onChange={(e) =>
          onChange(
            prefix === 'from'
              ? { fromMonth: Number(e.target.value) }
              : { toMonth: Number(e.target.value) },
          )
        }
      />
      <Input
        type="number"
        min={1}
        max={31}
        className="w-16"
        value={day}
        onChange={(e) =>
          onChange(
            prefix === 'from'
              ? { fromDay: Number(e.target.value) }
              : { toDay: Number(e.target.value) },
          )
        }
      />
    </span>
  )
}
