import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import axios from 'axios'
import {
  Alert,
  Button,
  Modal,
  TextArea,
  toast,
} from '@/ui'
import { ArrowsClockwiseIcon, CheckSquareIcon } from '@phosphor-icons/react'
import type { DictionaryDetail, SchemaDraft } from '@/api/client'
import { useCreateSchemaDraft, useUpdateSchemaDraft } from '@/api/mutations'
import { JsonEditor } from '@/components/workflow/JsonEditor'

type Props = {
  open: boolean
  onClose: () => void
  detail: DictionaryDetail
  /**
   * Edit mode: если передан существующий draft, modal работает как
   * редактор (PATCH /drafts/{id}) вместо create. Initial JSON и reason
   * seeded из draft'а. Используется когда maker правит draft после
   * CHANGES_REQUESTED — backend auto-transition CHANGES_REQUESTED → DRAFT.
   */
  editDraft?: SchemaDraft
  /**
   * Optional pre-filled proposed schema. When passed (and not in edit mode),
   * the JSON editor seeds with this instead of `detail.schemaJson`. Used by
   * the "Создать черновик с моими изменениями" CTA in DictionaryEditorDialog:
   * the maker tweaks the schema, hits the disabled Save, then jumps straight
   * into the draft modal with their in-progress edits already loaded. No
   * retyping.
   */
  initialProposedSchema?: unknown
  /** Callback после create — parent может закрыть modal + открыть drawer. */
  onCreated?: (draftId: string) => void
}

/**
 * Создание schema draft'а с editor'ом proposed schema. Минимальная версия:
 * JSON textarea с {@code Format} и {@code Validate} buttons. Phase 1d
 * может upgrade'ить до Monaco / visual schema editor.
 *
 * <p>Initial value seeded из текущего {@code detail.schemaJson}. User editing
 * правит proposedSchema, добавляет {@code reason}, submit'ит.
 *
 * <p>Backend errors surface'ятся как toast:
 * <ul>
 *   <li>409 {@code version_mismatch} — кто-то опубликовал свой draft пока
 *       этот пользователь готовился. Refresh page + retry.</li>
 *   <li>409 {@code active_draft_exists} — уже есть non-terminal draft.
 *       Пользователь должен open existing.</li>
 *   <li>400 {@code invalid_schema} — proposedSchema не валидна. Backend
 *       вернёт detail в message.</li>
 * </ul>
 */
/**
 * Resolve the schema JSON to seed the editor with. Three-tier priority:
 *
 *   1. edit-mode draft   — `editDraft.proposedSchema`
 *      Maker is revising their own draft after CHANGES_REQUESTED; the
 *      draft itself is the source of truth.
 *
 *   2. caller pre-fill   — `initialProposedSchema` prop
 *      DictionaryEditorDialog's inline CTA passes in-progress edits so
 *      the maker doesn't retype them.
 *
 *   3. live HEAD         — `detailSchemaJson`
 *      First-time draft on this dict with no pre-fill; start from the
 *      current published schema.
 *
 * Exported so the priority can be unit-tested without rendering the
 * whole modal (mutations + queries make the render test setup heavy).
 */
export function pickSeedSchema(
  editDraft: SchemaDraft | undefined,
  initialProposedSchema: unknown,
  detailSchemaJson: unknown,
): unknown {
  if (editDraft) return editDraft.proposedSchema
  if (initialProposedSchema !== undefined) return initialProposedSchema
  return detailSchemaJson
}

export function CreateSchemaDraftModal({
  open,
  onClose,
  detail,
  editDraft,
  initialProposedSchema,
  onCreated,
}: Props) {
  const { t } = useTranslation()
  const isEdit = Boolean(editDraft)
  const initialJson = useMemo(
    () =>
      JSON.stringify(
        pickSeedSchema(editDraft, initialProposedSchema, detail.schemaJson),
        null,
        2,
      ),
    [editDraft, initialProposedSchema, detail.schemaJson],
  )
  const initialReason = editDraft?.reason ?? ''
  const [json, setJson] = useState(initialJson)
  const [reason, setReason] = useState(initialReason)
  const [parseError, setParseError] = useState<string | null>(null)

  // Reset state при re-open чтобы не показывать stale данные с прошлого dict'а.
  useEffect(() => {
    if (open) {
      setJson(initialJson)
      setReason(initialReason)
      setParseError(null)
    }
  }, [open, initialJson, initialReason])

  const createMut = useCreateSchemaDraft(detail.name)
  const updateMut = useUpdateSchemaDraft(detail.name)
  const pending = createMut.isPending || updateMut.isPending

  const handleFormat = () => {
    try {
      const parsed = JSON.parse(json)
      setJson(JSON.stringify(parsed, null, 2))
      setParseError(null)
    } catch (e) {
      setParseError((e as Error).message)
    }
  }

  const handleValidate = () => {
    try {
      const parsed = JSON.parse(json)
      if (typeof parsed !== 'object' || parsed === null) {
        setParseError(
          t('createDraft.notObject', {
            defaultValue: 'Schema должна быть JSON object',
          }),
        )
        return false
      }
      setParseError(null)
      toast.success(
        t('createDraft.validationPassed', { defaultValue: 'JSON валиден' }),
      )
      return true
    } catch (e) {
      setParseError((e as Error).message)
      return false
    }
  }

  const handleSubmit = () => {
    let parsed: unknown
    try {
      parsed = JSON.parse(json)
    } catch (e) {
      setParseError((e as Error).message)
      return
    }
    const handleErrorToast = (err: unknown, fallbackKey: string, fallbackText: string) => {
      let msg = t(fallbackKey, { defaultValue: fallbackText })
      if (axios.isAxiosError(err)) {
        const body = err.response?.data as { code?: string; message?: string } | undefined
        msg = body?.message ?? body?.code ?? msg
      }
      toast.error(msg)
    }

    if (isEdit && editDraft) {
      updateMut.mutate(
        {
          draftId: editDraft.draftId,
          body: { proposedSchema: parsed, reason: reason || undefined },
        },
        {
          onSuccess: () => {
            toast.success(
              t('createDraft.toast.updated', { defaultValue: 'Черновик обновлён' }),
            )
            onClose()
          },
          onError: (err) =>
            handleErrorToast(
              err,
              'createDraft.toast.updateFailed',
              'Не удалось обновить черновик',
            ),
        },
      )
      return
    }

    createMut.mutate(
      {
        fromVersion: detail.schemaVersion,
        reason: reason || undefined,
        proposedSchema: parsed,
      },
      {
        onSuccess: (draft) => {
          toast.success(
            t('createDraft.toast.created', { defaultValue: 'Черновик создан' }),
          )
          onCreated?.(draft.draftId)
          onClose()
        },
        onError: (err) =>
          handleErrorToast(
            err,
            'createDraft.toast.failed',
            'Не удалось создать черновик',
          ),
      },
    )
  }

  return (
    <Modal
      isOpen={open}
      onClose={onClose}
      size="xl"
      title={
        isEdit
          ? t('createDraft.editTitle', { defaultValue: 'Редактировать черновик' })
          : t('createDraft.title', { defaultValue: 'Создать черновик схемы' })
      }
      description={
        isEdit && editDraft
          ? `${detail.name} · черновик от v${editDraft.branchedFromVersion}`
          : `${detail.name} · v${detail.schemaVersion}`
      }
    >
      <div className="space-y-4">
        <Alert variant="info">
          {isEdit
            ? t('createDraft.editIntro', {
                defaultValue:
                  'После сохранения статус черновика вернётся в draft — отправьте его на ревью заново.',
              })
            : t('createDraft.intro', {
                defaultValue:
                  'Изменения сохранятся как черновик от текущей версии. Live-схема не меняется до публикации после ревью.',
              })}
        </Alert>

        {/* Reason */}
        <div>
          <label className="block text-cap text-mute font-display uppercase tracking-wider mb-1">
            {t('createDraft.reason', { defaultValue: 'Причина изменений' })}
          </label>
          <TextArea
            value={reason}
            onChange={(e) => setReason(e.target.value)}
            placeholder={t('createDraft.reasonPlaceholder', {
              defaultValue: 'Зачем эти изменения нужны (видно ревьюеру)',
            })}
            rows={2}
            disabled={pending}
          />
        </div>

        {/* JSON Schema editor */}
        <div>
          <div className="flex items-center justify-between mb-1">
            <label className="text-cap text-mute font-display uppercase tracking-wider">
              {t('createDraft.schema', { defaultValue: 'Схема (JSON)' })}
            </label>
            <div className="flex items-center gap-1">
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={handleFormat}
                disabled={pending}
                leftIcon={<ArrowsClockwiseIcon weight="regular" size={14} />}
              >
                {t('createDraft.format', { defaultValue: 'Format' })}
              </Button>
              <Button
                type="button"
                variant="ghost"
                size="sm"
                onClick={handleValidate}
                disabled={pending}
                leftIcon={<CheckSquareIcon weight="regular" size={14} />}
              >
                {t('createDraft.validate', { defaultValue: 'Validate' })}
              </Button>
            </div>
          </div>
          {/* Monaco editor — lazy-loaded (~1.2MB gzip). Syntax highlight,
              bracket matching, fold/unfold по структуре JSON. Высота 420px —
              средняя schema (~30-50 полей) помещается без вертикального
              скролла. */}
          <JsonEditor
            value={json}
            onChange={(v) => {
              setJson(v)
              if (parseError) setParseError(null)
            }}
            disabled={pending}
            height={420}
          />
          {parseError && (
            <Alert variant="error" className="mt-2">
              <span className="font-mono text-cell">{parseError}</span>
            </Alert>
          )}
        </div>

        {/* Footer actions */}
        <div className="flex items-center justify-end gap-2 pt-2 border-t border-line">
          <Button
            type="button"
            variant="ghost"
            onClick={onClose}
            disabled={pending}
          >
            {t('common.cancel', { defaultValue: 'Отмена' })}
          </Button>
          <Button
            type="button"
            variant="primary"
            onClick={handleSubmit}
            disabled={pending || Boolean(parseError)}
          >
            {pending
              ? isEdit
                ? t('createDraft.updating', { defaultValue: 'Сохранение…' })
                : t('createDraft.creating', { defaultValue: 'Создание…' })
              : isEdit
                ? t('createDraft.submitEdit', { defaultValue: 'Сохранить' })
                : t('createDraft.submit', { defaultValue: 'Создать черновик' })}
          </Button>
        </div>
      </div>
    </Modal>
  )
}
