import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { MemoryRouter } from 'react-router-dom'
import { CreateRolForm, EditRolForm } from '../../../features/roles/components/RolForm'
import type { RolDto } from '../../../features/roles/api/types'

const API_URL = 'http://localhost:5000'

const mockCreated = {
  id: 10,
  codigo: 'cajero_senior',
  nombre: 'Cajero Senior',
  descripcion: 'Con más permisos',
  activo: true,
}

const server = setupServer()

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

function renderCreate(onSuccess = vi.fn()) {
  const qc = new QueryClient({ defaultOptions: { mutations: { retry: false } } })
  return render(
    <QueryClientProvider client={qc}>
      <MemoryRouter>
        <CreateRolForm onSuccess={onSuccess} />
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

function renderEdit(initial: RolDto, onSuccess = vi.fn()) {
  const qc = new QueryClient({ defaultOptions: { mutations: { retry: false } } })
  return render(
    <QueryClientProvider client={qc}>
      <MemoryRouter>
        <EditRolForm initial={initial} onSuccess={onSuccess} />
      </MemoryRouter>
    </QueryClientProvider>,
  )
}

describe('CreateRolForm — zod validation', () => {
  it('shows error when codigo is too short (< 3 chars)', async () => {
    const u = userEvent.setup()
    renderCreate()

    await u.type(screen.getByLabelText(/código/i), 'ab')
    await u.type(screen.getByLabelText(/nombre/i), 'Test')
    await u.click(screen.getByRole('button', { name: /crear rol/i }))

    await waitFor(() => {
      expect(screen.getByText(/mínimo 3 caracteres/i)).toBeInTheDocument()
    })
  })

  it('shows error for uppercase codigo', async () => {
    const u = userEvent.setup()
    renderCreate()

    await u.type(screen.getByLabelText(/código/i), 'Cajero')
    await u.type(screen.getByLabelText(/nombre/i), 'Test')
    await u.click(screen.getByRole('button', { name: /crear rol/i }))

    await waitFor(() => {
      expect(screen.getByText(/solo minúsculas/i)).toBeInTheDocument()
    })
  })

  it('shows error when nombre is empty', async () => {
    const u = userEvent.setup()
    renderCreate()

    await u.type(screen.getByLabelText(/código/i), 'cajero_test')
    await u.click(screen.getByRole('button', { name: /crear rol/i }))

    await waitFor(() => {
      expect(screen.getByText(/el nombre es requerido/i)).toBeInTheDocument()
    })
  })
})

describe('CreateRolForm — submit', () => {
  it('posts to /api/v1/roles and calls onSuccess on 201', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/roles`, async () => HttpResponse.json(mockCreated, { status: 201 })),
    )

    const onSuccess = vi.fn()
    const u = userEvent.setup()
    renderCreate(onSuccess)

    await u.type(screen.getByLabelText(/código/i), 'cajero_senior')
    await u.type(screen.getByLabelText(/nombre/i), 'Cajero Senior')
    await u.type(screen.getByLabelText(/descripción/i), 'Con más permisos')
    await u.click(screen.getByRole('button', { name: /crear rol/i }))

    await waitFor(() => expect(onSuccess).toHaveBeenCalled())
  })

  it('shows 409 rol_already_exists error', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/roles`, async () =>
        HttpResponse.json(
          { error: 'rol_already_exists', message: "El rol 'cajero' ya existe." },
          { status: 409 },
        ),
      ),
    )

    const u = userEvent.setup()
    renderCreate()

    await u.type(screen.getByLabelText(/código/i), 'cajero')
    await u.type(screen.getByLabelText(/nombre/i), 'Duplicated')
    await u.click(screen.getByRole('button', { name: /crear rol/i }))

    await waitFor(() => {
      expect(screen.getByRole('alert')).toHaveTextContent(/ya existe/i)
    })
  })
})

describe('EditRolForm', () => {
  const initial: RolDto = {
    id: 5,
    codigo: 'picadora',
    nombre: 'Picadora',
    descripcion: 'Edición de textos',
    activo: true,
    fechaCreacion: '2026-04-15T00:00:00Z',
    fechaModificacion: null,
  }

  it('shows initial values and renders codigo as read-only', () => {
    renderEdit(initial)

    expect(screen.getByDisplayValue('picadora')).toHaveAttribute('readOnly')
    expect(screen.getByDisplayValue('Picadora')).toBeInTheDocument()
    expect(screen.getByDisplayValue('Edición de textos')).toBeInTheDocument()
    expect(screen.getByRole('checkbox', { name: /activo/i })).toBeChecked()
  })

  it('submits PUT to /api/v1/roles/{codigo} with updated values', async () => {
    const updated: RolDto = { ...initial, nombre: 'Picadora V2', fechaModificacion: '2026-04-15T10:00:00Z' }
    server.use(
      http.put(`${API_URL}/api/v1/roles/picadora`, async () => HttpResponse.json(updated, { status: 200 })),
    )

    const onSuccess = vi.fn()
    const u = userEvent.setup()
    renderEdit(initial, onSuccess)

    const nombreInput = screen.getByDisplayValue('Picadora')
    await u.clear(nombreInput)
    await u.type(nombreInput, 'Picadora V2')
    await u.click(screen.getByRole('button', { name: /guardar cambios/i }))

    await waitFor(() => expect(onSuccess).toHaveBeenCalled())
  })
})
