import { useState, useEffect } from 'react'
import { isAxiosError } from 'axios'
import { toast } from 'sonner'
import { AlertCircle } from 'lucide-react'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { useUserPermisos } from '../hooks/useUserPermisos'
import { useUpdateUserPermisosOverrides } from '../hooks/useUpdateUserPermisosOverrides'
import { usePermisos } from '@/features/permisos/hooks/usePermisos'
import type { PermisoOverrideState } from '../types'
import type { PermisoDto } from '@/features/permisos/api/types'

interface PermisosEditorProps {
  userId: number
}

function groupByModulo(permisos: PermisoDto[]): Map<string, PermisoDto[]> {
  const map = new Map<string, PermisoDto[]>()
  for (const p of permisos) {
    const modulo = p.codigo.split(':')[0] ?? p.modulo
    if (!map.has(modulo)) map.set(modulo, [])
    map.get(modulo)!.push(p)
  }
  return map
}

function resolveErrorMessage(err: unknown): string {
  if (isAxiosError(err) && err.response?.data) {
    const data = err.response.data as { title?: string; invalidCodes?: string[]; overlap?: string[] }
    if (data.title === 'invalid-permiso-codes') {
      const codes = data.invalidCodes?.join(', ') ?? ''
      return `Códigos de permiso inválidos: ${codes}`
    }
    if (data.title === 'grant-deny-overlap') {
      const codes = data.overlap?.join(', ') ?? ''
      return `Los siguientes permisos están en grant y deny al mismo tiempo: ${codes}`
    }
    return 'No se pudieron guardar los cambios.'
  }
  return 'No se pudieron guardar los cambios.'
}

export function PermisosEditor({ userId }: PermisosEditorProps) {
  const { data: permisoData, isLoading: loadingPermisos } = useUserPermisos(userId)
  const { data: catalogo, isLoading: loadingCatalogo } = usePermisos()
  const mutation = useUpdateUserPermisosOverrides(userId)

  // Map<codigopermiso, PermisoOverrideState>
  const [states, setStates] = useState<Map<string, PermisoOverrideState>>(new Map())
  const [saveError, setSaveError] = useState<string | null>(null)

  // Initialize state from loaded data
  useEffect(() => {
    if (!permisoData) return
    const map = new Map<string, PermisoOverrideState>()
    // Start all known codes as 'heredado'
    for (const c of permisoData.rolPermisos) map.set(c, 'heredado')
    // Apply grant overrides
    for (const c of permisoData.overrides.grant) map.set(c, 'concedido')
    // Apply deny overrides
    for (const c of permisoData.overrides.deny) map.set(c, 'denegado')
    // eslint-disable-next-line react-hooks/set-state-in-effect -- sincroniza prop externa (permisoData) con mapa local de overrides; patrón válido de derived state
    setStates(map)
    setSaveError(null)
  }, [permisoData])

  if (loadingPermisos || loadingCatalogo) {
    return <p className="text-sm text-muted-foreground">Cargando permisos...</p>
  }

  if (!permisoData || !catalogo) {
    return (
      <Alert variant="destructive">
        <AlertCircle className="h-4 w-4" />
        <AlertDescription>No se pudieron cargar los permisos.</AlertDescription>
      </Alert>
    )
  }

  // Build complete set of all relevant permission codes from catalog
  // Filter catalog to only show permisos that appear in rolPermisos, grant, deny, or all catalog
  const allCodes = new Set([
    ...permisoData.rolPermisos,
    ...permisoData.overrides.grant,
    ...permisoData.overrides.deny,
  ])

  // Use catalog for grouping and names, showing all permisos known plus any from overrides
  const relevantPermisos = catalogo.filter(
    (p) => allCodes.has(p.codigo) || catalogo.length <= 30,
  )

  const grupos = groupByModulo(relevantPermisos)

  function getState(codigo: string): PermisoOverrideState {
    return states.get(codigo) ?? 'heredado'
  }

  function setState(codigo: string, state: PermisoOverrideState) {
    setSaveError(null)
    setStates((prev) => {
      const next = new Map(prev)
      next.set(codigo, state)
      return next
    })
  }

  function handleSave() {
    const grant: string[] = []
    const deny: string[] = []

    for (const [codigo, state] of states.entries()) {
      if (state === 'concedido') grant.push(codigo)
      else if (state === 'denegado') deny.push(codigo)
    }

    mutation.mutate(
      { grant, deny },
      {
        onError: (err) => {
          const msg = resolveErrorMessage(err)
          setSaveError(msg)
          toast.error(msg)
        },
        onSuccess: () => {
          setSaveError(null)
          toast.success('Permisos actualizados correctamente')
        },
      },
    )
  }

  return (
    <div className="space-y-6">
      {saveError && (
        <Alert variant="destructive" role="alert">
          <AlertCircle className="h-4 w-4" />
          <AlertDescription>{saveError}</AlertDescription>
        </Alert>
      )}

      {Array.from(grupos.entries()).map(([modulo, permisos]) => (
        <section key={modulo}>
          <h3 className="text-sm font-semibold uppercase tracking-wider text-muted-foreground mb-3 capitalize">
            {modulo}
          </h3>
          <div className="space-y-2">
            {permisos.map((p) => {
              const currentState = getState(p.codigo)
              return (
                <div
                  key={p.codigo}
                  data-testid={`permiso-row-${p.codigo}`}
                  className="flex items-center justify-between rounded-md border border-border px-3 py-2"
                >
                  <div className="flex flex-col min-w-0 mr-4">
                    <span className="text-sm font-medium">{p.nombre}</span>
                    <span className="font-mono text-xs text-muted-foreground/70">{p.codigo}</span>
                  </div>
                  <div className="flex items-center gap-1 shrink-0">
                    {(['heredado', 'concedido', 'denegado'] as PermisoOverrideState[]).map(
                      (state) => (
                        <button
                          key={state}
                          type="button"
                          role="button"
                          aria-label={state.charAt(0).toUpperCase() + state.slice(1)}
                          aria-pressed={currentState === state}
                          onClick={() => setState(p.codigo, state)}
                          className={`
                            px-2 py-1 rounded text-xs font-medium transition-colors capitalize
                            ${
                              currentState === state
                                ? state === 'heredado'
                                  ? 'bg-secondary text-secondary-foreground'
                                  : state === 'concedido'
                                    ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100'
                                    : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100'
                                : 'bg-transparent text-muted-foreground hover:bg-accent'
                            }
                          `}
                        >
                          {state}
                        </button>
                      ),
                    )}
                  </div>
                </div>
              )
            })}
          </div>
        </section>
      ))}

      <div className="flex justify-end pt-2">
        <Button onClick={handleSave} disabled={mutation.isPending}>
          {mutation.isPending ? 'Guardando...' : 'Guardar cambios'}
        </Button>
      </div>
    </div>
  )
}
