import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowsClockwiseIcon, WarningIcon } from '@phosphor-icons/react'
import { Dialog, DialogContent, DialogTitle, DialogDescription } from '@/ui'
import { useRecordRaw } from '@/api/queries'
import type { CreateRecordRequest } from '@/api/client'
import { cn } from '@/lib/utils'

/**
 * ConflictDiffModal — handoff line 444 ("Save → optimistic update + toast;
 * conflict → modal with diff").
 *
 * <p>Опoшается когда PUT /records/{key} вернул 409 (optimistic-lock fail —
 * другой пользователь сохранил тот же record между нашим load и submit'ом).
 *
 * <p>Сравнивает локальный payload (что юзер пытался сохранить) с серверным
 * текущим состоянием (refetch через useRecordRaw). Per-field diff list:
 * показываем только те properties у которых local≠server. Footer actions:
 * <ul>
 *   <li><b>Отменить</b> — close modal, оставить editor open (user сам решит)</li>
 *   <li><b>Загрузить серверное</b> — discard local edits, reload server state</li>
 *   <li><b>Перезаписать сервером</b> — force-apply local payload (retry without
 *       optimistic lock; backend всё ещё применит business rules). UX warning:
 *       это перетрёт чужие правки.</li>
 * </ul>
 */

export type ConflictDiffModalProps = {
  open: boolean
  onClose: () => void
  /** Local payload пользователя — что он только что попытался сохранить. */
  localPayload: CreateRecordRequest | null
  /** Dict name + business key для refetch server state. */
  dictionaryName: string
  businessKey: string | undefined
  /** "Загрузить серверное" — discard local, refetch & open editor с server data. */
  onReloadServer: () => void
  /** "Перезаписать" — force update (re-submit with current data, expecting backend
   * либо примет либо вернёт 409 ещё раз). */
  onOverwrite: () => void
}

export function ConflictDiffModal({
  open,
  onClose,
  localPayload,
  dictionaryName,
  businessKey,
  onReloadServer,
  onOverwrite,
}: ConflictDiffModalProps) {
  const { t } = useTranslation()
  const serverQuery = useRecordRaw(dictionaryName, open ? businessKey : undefined)

  // Diff: иди по union ключей local + server, оставь только отличающиеся.
  const diffRows = useMemo(() => {
    if (!localPayload || !serverQuery.data) return []
    const localData = (localPayload.data ?? {}) as Record<string, unknown>
    const serverData = (serverQuery.data.data ?? {}) as Record<string, unknown>
    const keys = new Set<string>([
      ...Object.keys(localData),
      ...Object.keys(serverData),
    ])
    const rows: { field: string; local: unknown; server: unknown }[] = []
    for (const k of keys) {
      const lv = localData[k]
      const sv = serverData[k]
      if (!valuesEqual(lv, sv)) {
        rows.push({ field: k, local: lv, server: sv })
      }
    }
    return rows.sort((a, b) => a.field.localeCompare(b.field))
  }, [localPayload, serverQuery.data])

  return (
    <Dialog open={open} onOpenChange={(v) => !v && onClose()}>
      <DialogContent size="lg" className="!p-0 gap-0 max-h-[85vh]">
        {/* Radix a11y: visually-hidden Title + Description satisfy
          * DialogContent requirements. Visible header — custom layout ниже. */}
        <DialogTitle className="sr-only">
          {t('conflict.title', { defaultValue: 'Запись изменена другим пользователем' })}
        </DialogTitle>
        <DialogDescription className="sr-only">
          {t('conflict.description', {
            defaultValue:
              'Сравнение локальных правок с актуальным состоянием на сервере.',
          })}
        </DialogDescription>
        {/* Header */}
        <div className="flex items-start gap-3 px-6 py-4 border-b border-line">
          <div className="size-8 rounded-md bg-warn-bg inline-flex items-center justify-center shrink-0">
            <WarningIcon weight="fill" size={18} className="text-warn" />
          </div>
          <div className="min-w-0 flex-1">
            <div className="text-cap text-mute tracking-[0.18em] uppercase">
              {t('conflict.label', { defaultValue: 'Конфликт сохранения' })}
              <span className="mx-2 text-line">·</span>
              <span className="text-ink">{dictionaryName.toUpperCase()}</span>
            </div>
            <h2 className="text-title-md text-ink font-semibold mt-0.5">
              {t('conflict.title', {
                defaultValue: 'Запись изменена другим пользователем',
              })}
            </h2>
            {businessKey && (
              <div className="text-mono text-cell text-mute mt-0.5">{businessKey}</div>
            )}
          </div>
        </div>

        {/* Diff body */}
        <div className="px-6 py-4 overflow-y-auto flex-1 min-h-0">
          {serverQuery.isLoading && (
            <div className="text-mute text-body py-8 text-center">
              {t('conflict.loading', {
                defaultValue: 'Загружаем актуальное состояние сервера…',
              })}
            </div>
          )}
          {serverQuery.error && (
            <div className="text-pink text-body py-4">
              {t('conflict.loadError', {
                defaultValue: 'Не удалось загрузить серверное состояние',
              })}
            </div>
          )}
          {!serverQuery.isLoading && !serverQuery.error && diffRows.length === 0 && (
            <div className="text-mute text-body py-8 text-center">
              {t('conflict.noFieldDiff', {
                defaultValue:
                  'Поля совпадают. Возможно конфликт на уровне метаданных (validFrom / dataScope).',
              })}
            </div>
          )}
          {diffRows.length > 0 && (
            <>
              <div className="grid grid-cols-[1fr,1fr,1fr] gap-2 mb-2 px-2">
                <div className="text-cap text-mute tracking-[0.16em] uppercase">
                  {t('conflict.col.field', { defaultValue: 'Поле' })}
                </div>
                <div className="text-cap text-mute tracking-[0.16em] uppercase">
                  {t('conflict.col.local', { defaultValue: 'Ваши изменения' })}
                </div>
                <div className="text-cap text-mute tracking-[0.16em] uppercase">
                  {t('conflict.col.server', { defaultValue: 'На сервере' })}
                </div>
              </div>
              <ul className="space-y-1">
                {diffRows.map((row) => (
                  <li
                    key={row.field}
                    className="grid grid-cols-[1fr,1fr,1fr] gap-2 px-2 py-2 rounded-md border border-line bg-surface text-body"
                  >
                    <div className="text-mono text-ink truncate" title={row.field}>
                      {row.field}
                    </div>
                    <div className="font-mono text-cell text-accent break-words bg-accent-bg/30 rounded-sm px-1.5 py-0.5">
                      {formatValue(row.local)}
                    </div>
                    <div className="font-mono text-cell text-ink-2 break-words bg-surface-2 rounded-sm px-1.5 py-0.5">
                      {formatValue(row.server)}
                    </div>
                  </li>
                ))}
              </ul>
            </>
          )}
        </div>

        {/* Footer */}
        <div className="px-6 py-3 border-t border-line flex items-center justify-between gap-3 flex-wrap mt-auto">
          <div className="text-cap text-mute tracking-[0.14em] uppercase max-w-md">
            {t('conflict.disclaimer', {
              defaultValue:
                'Перезапись потеряет чужие изменения. Загрузить — потеряет ваши.',
            })}
          </div>
          <div className="flex items-center gap-2">
            <button
              type="button"
              onClick={onClose}
              className="h-9 px-4 rounded-md border border-line bg-surface text-ink-2 hover:bg-surface-2 hover:border-line-2 text-body transition-colors"
            >
              {t('common.cancel', { defaultValue: 'Отмена' })}
            </button>
            <button
              type="button"
              onClick={onReloadServer}
              className="h-9 px-4 rounded-md border border-line bg-surface text-ink-2 hover:bg-surface-2 hover:border-ink-2 text-body inline-flex items-center gap-1.5 transition-colors"
            >
              <ArrowsClockwiseIcon weight="regular" size={14} />
              {t('conflict.reload', { defaultValue: 'Загрузить серверное' })}
            </button>
            <button
              type="button"
              onClick={onOverwrite}
              className={cn(
                'h-9 px-4 rounded-md bg-pink text-on-accent hover:opacity-90 text-body font-semibold transition-opacity',
              )}
            >
              {t('conflict.overwrite', { defaultValue: 'Перезаписать' })}
            </button>
          </div>
        </div>
      </DialogContent>
    </Dialog>
  )
}

/** Deep-equality для JSON-safe values (string/number/bool/null/array/object). */
function valuesEqual(a: unknown, b: unknown): boolean {
  if (a === b) return true
  if (a === null || b === null) return a === b
  if (typeof a !== typeof b) return false
  if (typeof a === 'object') {
    try {
      return JSON.stringify(a) === JSON.stringify(b)
    } catch {
      return false
    }
  }
  return false
}

/** Render value as a short readable string. Objects → JSON, undefined → «—». */
function formatValue(v: unknown): string {
  if (v === undefined || v === null || v === '') return '—'
  if (typeof v === 'string') return v
  if (typeof v === 'number' || typeof v === 'boolean') return String(v)
  try {
    const s = JSON.stringify(v)
    return s.length > 120 ? s.slice(0, 117) + '…' : s
  } catch {
    return String(v)
  }
}
