import { describe, it, expect, vi, beforeAll, afterAll, afterEach } 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 React from 'react'
import { ProductFormDialog } from '../../../features/products/components/ProductFormDialog'
import type { ProductDetail } from '../../../features/products/types'
import type { PagedResult } from '../../../features/product-types/types'
import type { ProductTypeListItem } from '../../../features/product-types/types'

vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn() } }))

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

const ptSimple: ProductTypeListItem = {
  id: 2,
  nombre: 'Simple',
  hasDuration: false,
  requiresText: false,
  requiresCategory: false,
  isBundle: false,
  allowImages: false,
  isActive: true,
}

const mockProductTypesPaged: PagedResult<ProductTypeListItem> = {
  items: [ptSimple],
  page: 1,
  pageSize: 50,
  total: 1,
}

const mockProduct: ProductDetail = {
  id: 1,
  nombre: 'Clasificado Estándar',
  medioId: 1,
  productTypeId: 2,
  rubroId: null,
  basePrice: 100.5,
  priceDurationDays: null,
  isActive: true,
  fechaCreacion: '2026-04-19T00:00:00Z',
  fechaModificacion: null,
}

const server = setupServer()

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

function wrap(children: React.ReactNode) {
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  return render(
    <QueryClientProvider client={qc}>
      <MemoryRouter>{children}</MemoryRouter>
    </QueryClientProvider>,
  )
}

function withProductTypesHandler() {
  server.use(
    http.get(`${API_URL}/api/v1/product-types`, () =>
      HttpResponse.json(mockProductTypesPaged),
    ),
  )
}

// ─── ProductFormDialog — create mode ─────────────────────────────────────

describe('ProductFormDialog — create mode', () => {
  it('renders create dialog title when no product prop', () => {
    withProductTypesHandler()
    wrap(<ProductFormDialog open={true} onOpenChange={vi.fn()} />)
    expect(screen.getByRole('heading', { name: /nuevo producto/i })).toBeInTheDocument()
  })

  it('renders DialogDescription (accessibility)', () => {
    withProductTypesHandler()
    wrap(<ProductFormDialog open={true} onOpenChange={vi.fn()} />)
    expect(screen.getByText(/completá los datos/i)).toBeInTheDocument()
  })

  it('calls create mutation and closes dialog on success', async () => {
    withProductTypesHandler()
    server.use(
      http.post(`${API_URL}/api/v1/admin/products`, () =>
        HttpResponse.json({ id: 10, nombre: 'Nuevo Producto' }, { status: 201 }),
      ),
    )
    const onOpenChange = vi.fn()
    wrap(<ProductFormDialog open={true} onOpenChange={onOpenChange} />)
    await userEvent.type(screen.getByLabelText(/nombre/i), 'Nuevo Producto')
    await userEvent.type(screen.getByLabelText(/id de medio/i), '1')
    await userEvent.type(screen.getByLabelText(/id de tipo de producto/i), '2')
    await userEvent.type(screen.getByLabelText(/precio base/i), '100')
    await userEvent.click(screen.getByRole('button', { name: /guardar/i }))
    await waitFor(() => {
      expect(onOpenChange).toHaveBeenCalledWith(false)
    })
  })

  it('shows inline error when backend returns 409', async () => {
    withProductTypesHandler()
    server.use(
      http.post(`${API_URL}/api/v1/admin/products`, () =>
        HttpResponse.json(
          { error: 'product_nombre_duplicado', message: 'Ya existe un producto con ese nombre' },
          { status: 409 },
        ),
      ),
    )
    wrap(<ProductFormDialog open={true} onOpenChange={vi.fn()} />)
    await userEvent.type(screen.getByLabelText(/nombre/i), 'Duplicado')
    await userEvent.type(screen.getByLabelText(/id de medio/i), '1')
    await userEvent.type(screen.getByLabelText(/id de tipo de producto/i), '2')
    await userEvent.type(screen.getByLabelText(/precio base/i), '100')
    await userEvent.click(screen.getByRole('button', { name: /guardar/i }))
    await waitFor(() => {
      expect(screen.getByText(/ya existe un producto con ese nombre/i)).toBeInTheDocument()
    })
  })

  it('shows inline error when backend returns 409 ProductTypeInactivo', async () => {
    withProductTypesHandler()
    server.use(
      http.post(`${API_URL}/api/v1/admin/products`, () =>
        HttpResponse.json(
          { error: 'product_type_inactivo', message: 'El tipo de producto está inactivo' },
          { status: 409 },
        ),
      ),
    )
    wrap(<ProductFormDialog open={true} onOpenChange={vi.fn()} />)
    await userEvent.type(screen.getByLabelText(/nombre/i), 'Prod Test')
    await userEvent.type(screen.getByLabelText(/id de medio/i), '1')
    await userEvent.type(screen.getByLabelText(/id de tipo de producto/i), '2')
    await userEvent.type(screen.getByLabelText(/precio base/i), '100')
    await userEvent.click(screen.getByRole('button', { name: /guardar/i }))
    await waitFor(() => {
      expect(screen.getByText(/tipo de producto está inactivo/i)).toBeInTheDocument()
    })
  })
})

// ─── ProductFormDialog — edit mode ────────────────────────────────────────

describe('ProductFormDialog — edit mode', () => {
  it('renders edit dialog title and pre-fills nombre', () => {
    withProductTypesHandler()
    wrap(
      <ProductFormDialog
        open={true}
        onOpenChange={vi.fn()}
        product={mockProduct}
      />,
    )
    expect(screen.getByRole('heading', { name: /editar producto/i })).toBeInTheDocument()
    const input = screen.getByLabelText(/nombre/i) as HTMLInputElement
    expect(input.value).toBe('Clasificado Estándar')
  })

  it('calls update mutation and closes dialog on success', async () => {
    withProductTypesHandler()
    server.use(
      http.put(`${API_URL}/api/v1/admin/products/1`, () =>
        HttpResponse.json({ ...mockProduct, nombre: 'Modificado' }),
      ),
    )
    const onOpenChange = vi.fn()
    wrap(
      <ProductFormDialog
        open={true}
        onOpenChange={onOpenChange}
        product={mockProduct}
      />,
    )
    const input = screen.getByLabelText(/nombre/i) as HTMLInputElement
    await userEvent.clear(input)
    await userEvent.type(input, 'Modificado')
    await userEvent.click(screen.getByRole('button', { name: /guardar/i }))
    await waitFor(() => {
      expect(onOpenChange).toHaveBeenCalledWith(false)
    })
  })
})
