/**
 * Formulaire de saisie pour une simulation Expression Écrite.
 *
 * SEC-04 : validation Zod avant envoi (texte non vide, max 5 000 caractères).
 * SEC-05 : aucun dangerouslySetInnerHTML — le texte utilisateur est rendu comme texte.
 * Règle H : aucune logique métier — délègue à simulationConfig + useTimer.
 *
 * Minuteur et cibles de mots : cf. getSimulationConfig(tache).
 * À l'expiration du timer, soumission automatique si mots ≥ motsMin,
 * sinon message explicite demandant d'atteindre le seuil.
 */

import { useEffect, useRef, useState, type FormEvent } from 'react'
import { Clock, Lightbulb, Loader2, Shuffle } from 'lucide-react'
import { z } from 'zod'
import { Button } from '@/shared/ui/Button'
import { formatTache } from '@/entities/production/lib'
import { hasAccess, type Plan } from '@/entities/user/lib'
import type { SujetData, Tache } from '@/entities/production/types'
import type { NclcCible } from '@/entities/report/types'
import type { ApiError } from '@/shared/types/api'
import { countWords, getSimulationConfig } from '../lib/simulationConfig'
import { useTimer } from '../hooks/useTimer'
import { useIdees } from '../hooks/useIdees'
import { useAutosave } from '../hooks/useAutosave'
import type { SimulationStep } from '../state/simulationFlow'
import { SujetDisplay } from './SujetDisplay'
import { SpecialCharsKeyboard } from './SpecialCharsKeyboard'
import { TimerDisplay } from './TimerDisplay'
import { WordCountBar } from './WordCountBar'
import { IdeesSuggestions } from './IdeesSuggestions'
import { NclcCibleSelector } from './NclcCibleSelector'

const MIN_WORDS_IDEES = 30
const LS_SIMULATION_ID_KEY = 'expria_simulation_id'

const secondaryActionBtn =
  'inline-flex items-center gap-1.5 rounded-md border border-line bg-surface px-3 py-1.5 text-sm text-ink-2 transition-colors hover:border-expria hover:text-expria focus:border-expria focus:outline-none focus:shadow-focus disabled:cursor-not-allowed disabled:opacity-50'

const textSchema = z.object({
  texte: z
    .string()
    .min(1, 'Le texte ne peut pas être vide.')
    .max(5000, 'Le texte ne doit pas dépasser 5 000 caractères.'),
})

function mapCorrectError(err: ApiError | null): string | null {
  if (!err) return null
  switch (err.code) {
    case 'SIMULATION_NOT_FOUND':
      return 'Simulation introuvable. Revenez en arrière et recommencez.'
    case 'AUTH_REQUIRED':
      return 'Votre session a expiré. Reconnectez-vous.'
    case 'VALIDATION_ERROR':
    case 'INVALID_BODY':
      return 'Le texte soumis est invalide. Vérifiez votre saisie.'
    default:
      return 'Correction impossible. Réessayez dans quelques instants.'
  }
}

interface Props {
  tache: Tache
  sujet: SujetData | null
  plan: Plan
  simulationId: string
  initialContenu?: string
  step: SimulationStep
  isSubmitting: boolean
  error: ApiError | null
  onSubmit: (texte: string, nclcCible: NclcCible) => void
  onBack: () => void
  onChangeSujet: () => void
}

export function SimulationForm({
  tache,
  sujet,
  plan,
  simulationId,
  initialContenu,
  step,
  isSubmitting,
  error,
  onSubmit,
  onBack,
  onChangeSujet,
}: Props) {
  const textareaRef = useRef<HTMLTextAreaElement>(null)
  const hasAutoSubmittedRef = useRef(false)
  const [texte, setTexte] = useState(() => initialContenu ?? '')
  const [fieldError, setFieldError] = useState<string | null>(null)
  const [isIdeesOpen, setIsIdeesOpen] = useState(false)
  const [nclcCible, setNclcCible] = useState<NclcCible>(9)

  const config = getSimulationConfig(tache)
  const wordCount = countWords(texte)
  const canSubmit = wordCount >= config.motsMin

  const timer = useTimer(config.dureeMinutes, !isSubmitting)
  const idees = useIdees()
  const autosave = useAutosave(simulationId, texte, !isSubmitting)

  // FTD-21 — marquer la simulation en cours pour le resume au refresh.
  useEffect(() => {
    if (simulationId) {
      localStorage.setItem(LS_SIMULATION_ID_KEY, simulationId)
    }
  }, [simulationId])

  // FTD-21 — nettoyer dès que la correction est terminée.
  useEffect(() => {
    if (step === 'done') {
      localStorage.removeItem(LS_SIMULATION_ID_KEY)
    }
  }, [step])

  const tipsAllowed = hasAccess(plan, 'tips')
  const ideesDisabled =
    isSubmitting || idees.isLoading || !sujet || !tipsAllowed || wordCount < MIN_WORDS_IDEES
  const ideesTitle = !tipsAllowed
    ? 'Disponible en Standard'
    : wordCount < MIN_WORDS_IDEES
      ? `Écrivez au moins ${MIN_WORDS_IDEES} mots`
      : undefined

  function handleIdeesClick() {
    if (!sujet) return
    setIsIdeesOpen(true)
    idees.fetchIdees({ consigne: sujet.consigne, contenu: texte })
  }

  function handleIdeesClose() {
    setIsIdeesOpen(false)
    idees.reset()
  }

  useEffect(() => {
    const el = textareaRef.current
    if (!el) return
    el.style.height = 'auto'
    el.style.height = `${el.scrollHeight}px`
  }, [texte])

  useEffect(() => {
    if (!timer.isExpired) return
    if (hasAutoSubmittedRef.current) return
    if (isSubmitting) return
    if (wordCount < config.motsMin) return

    hasAutoSubmittedRef.current = true
    onSubmit(texte, nclcCible)
  }, [timer.isExpired, wordCount, config.motsMin, isSubmitting, texte, nclcCible, onSubmit])

  function handleInsert(char: string) {
    const el = textareaRef.current
    if (!el) {
      setTexte((prev) => prev + char)
      return
    }
    const start = el.selectionStart ?? texte.length
    const end = el.selectionEnd ?? texte.length
    const next = texte.slice(0, start) + char + texte.slice(end)
    setTexte(next)
    const caret = start + char.length
    requestAnimationFrame(() => {
      el.focus()
      el.setSelectionRange(caret, caret)
    })
  }

  function handleSubmit(e: FormEvent<HTMLFormElement>) {
    e.preventDefault()
    setFieldError(null)

    const parsed = textSchema.safeParse({ texte })
    if (!parsed.success) {
      setFieldError(parsed.error.flatten().fieldErrors.texte?.[0] ?? null)
      return
    }

    if (wordCount < config.motsMin) {
      setFieldError(`Écrivez au moins ${config.motsMin} mots pour soumettre.`)
      return
    }

    onSubmit(parsed.data.texte, nclcCible)
  }

  const apiError = mapCorrectError(error)
  const expiredBelowMin = timer.isExpired && wordCount < config.motsMin
  const submitDisabled = isSubmitting || !canSubmit

  return (
    <div className="space-y-4">
      <div className="flex items-center gap-3">
        <button
          type="button"
          onClick={onBack}
          disabled={isSubmitting}
          className="text-sm text-ink-4 underline-offset-4 hover:text-ink-2 hover:underline disabled:pointer-events-none"
        >
          ← Retour
        </button>
        <h2 className="flex-1 text-lg font-semibold text-ink-1">{formatTache(tache)}</h2>
      </div>

      <SujetDisplay sujet={sujet} />

      {sujet && (
        <div className="flex flex-wrap justify-end gap-2">
          <button
            type="button"
            onClick={handleIdeesClick}
            disabled={ideesDisabled}
            title={ideesTitle}
            className={secondaryActionBtn}
            aria-label="Obtenir des suggestions d'idées"
          >
            <Lightbulb className="size-4" aria-hidden="true" />
            Suggestions d'idées
          </button>
          <button
            type="button"
            onClick={onChangeSujet}
            disabled={isSubmitting}
            className={secondaryActionBtn}
            aria-label="Changer de sujet"
          >
            <Shuffle className="size-4" aria-hidden="true" />
            Changer de sujet
          </button>
        </div>
      )}

      <IdeesSuggestions
        idees={idees.idees}
        isLoading={idees.isLoading}
        error={idees.error}
        isOpen={isIdeesOpen}
        onClose={handleIdeesClose}
      />

      {apiError && (
        <div
          role="alert"
          className="rounded-md border border-danger/40 bg-danger-bg px-3 py-2 text-sm text-danger"
        >
          {apiError}
        </div>
      )}

      {expiredBelowMin && (
        <div
          role="alert"
          className="rounded-md border border-warning/40 bg-warning-bg px-3 py-2 text-sm text-warning"
        >
          Temps écoulé. Écrivez au moins {config.motsMin} mots pour soumettre.
        </div>
      )}

      <form onSubmit={handleSubmit} className="space-y-3" noValidate>
        <div className="space-y-1.5">
          <label htmlFor="texte" className="text-sm font-medium text-ink-2">
            Votre production
          </label>
          <div className="sticky top-14 z-20 bg-canvas pb-1 lg:top-0">
            <div
              className={`mb-2 flex items-center gap-2 rounded-md border px-3 py-2 ${
                timer.isExpired || timer.secondesRestantes < 120
                  ? 'border-danger bg-danger-bg'
                  : 'border-line bg-surface'
              }`}
            >
              <Clock
                className={`size-4 ${
                  timer.isExpired || timer.secondesRestantes < 120 ? 'text-danger' : 'text-ink-3'
                }`}
                aria-hidden="true"
              />
              <span
                className={`text-xs font-medium uppercase tracking-wide ${
                  timer.isExpired || timer.secondesRestantes < 120 ? 'text-danger' : 'text-ink-3'
                }`}
              >
                Temps restant
              </span>
              <span className="ml-auto">
                <TimerDisplay
                  secondesRestantes={timer.secondesRestantes}
                  isExpired={timer.isExpired}
                />
              </span>
            </div>
            <SpecialCharsKeyboard onInsert={handleInsert} disabled={isSubmitting} />
          </div>
          <textarea
            ref={textareaRef}
            id="texte"
            rows={8}
            value={texte}
            onChange={(e) => setTexte(e.target.value)}
            disabled={isSubmitting}
            placeholder="Rédigez votre texte ici…"
            aria-invalid={!!fieldError}
            aria-describedby={fieldError ? 'texte-error' : undefined}
            className="w-full resize-none overflow-y-hidden rounded-md border border-line bg-surface p-3 text-sm text-ink-1 placeholder:text-ink-5 focus:border-expria focus:outline-none focus:shadow-focus disabled:cursor-not-allowed disabled:opacity-50"
          />
          <WordCountBar count={wordCount} config={config} />
          <NclcCibleSelector value={nclcCible} onChange={setNclcCible} disabled={isSubmitting} />

          {autosave.savedAt && !fieldError && (
            <p className="text-xs text-ink-4" aria-live="polite">
              Sauvegardé à{' '}
              {autosave.savedAt.toLocaleTimeString('fr-FR', {
                hour: '2-digit',
                minute: '2-digit',
              })}
            </p>
          )}
          {fieldError && (
            <p id="texte-error" className="text-sm text-danger">
              {fieldError}
            </p>
          )}
        </div>

        <Button type="submit" className="w-full" disabled={submitDisabled}>
          {isSubmitting ? (
            <>
              <Loader2 className="animate-spin" aria-hidden="true" />
              Correction en cours…
            </>
          ) : (
            'Soumettre ma production'
          )}
        </Button>

        {isSubmitting && (
          <p className="text-center text-xs text-ink-4">
            La correction peut prendre jusqu'à 30 secondes.
          </p>
        )}
      </form>
    </div>
  )
}
