import { describe, it, expect, vi, afterEach, beforeAll, afterAll } 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 React from 'react'
import { ChargeableCharsTable } from '../components/ChargeableCharsTable'
import type { ChargeableCharConfig } from '../types'

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

// Mock ProductTypeSelect to avoid fetching product-types in table tests
vi.mock('../components/ProductTypeSelect', () => ({
  ProductTypeSelect: ({ value, onValueChange, 'aria-label': ariaLabel }: {
    value: number | null | undefined
    onValueChange: (v: number | null | undefined) => void
    'aria-label'?: string
  }) => (
    <select
      aria-label={ariaLabel ?? 'Tipo de producto'}
      value={value === null ? '__global__' : value === undefined ? '__all__' : String(value)}
      onChange={(e) => {
        const v = e.target.value
        onValueChange(v === '__global__' ? null : v === '__all__' ? undefined : Number(v))
      }}
    >
      <option value="__all__">Todos los tipos</option>
      <option value="__global__">Global</option>
      <option value="1">Clasificados</option>
    </select>
  ),
}))

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

function makeConfig(overrides: Partial<ChargeableCharConfig> = {}): ChargeableCharConfig {
  return {
    id: 1,
    productTypeId: null,
    symbol: '$',
    category: 'Currency',
    pricePerUnit: 1.5,
    validFrom: '2026-01-01',
    validTo: null,
    isActive: true,
    ...overrides,
  }
}

const server = setupServer()
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }))
afterEach(() => { server.resetHandlers(); vi.clearAllMocks() })
afterAll(() => server.close())

function renderTable(
  configs: ChargeableCharConfig[],
  onSchedulePrice = vi.fn(),
  onDeactivate = vi.fn(),
) {
  const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } })
  return render(
    <QueryClientProvider client={qc}>
      <ChargeableCharsTable
        configs={configs}
        total={configs.length}
        page={1}
        pageSize={20}
        onPageChange={vi.fn()}
        productTypeId={undefined}
        activeOnly={true}
        onProductTypeChange={vi.fn()}
        onActiveOnlyChange={vi.fn()}
        onSchedulePrice={onSchedulePrice}
        onDeactivate={onDeactivate}
      />
    </QueryClientProvider>,
  )
}

describe('ChargeableCharsTable', () => {
  it('renders rows from query result — symbol and category visible', () => {
    renderTable([
      makeConfig({ id: 1, symbol: '$', category: 'Currency' }),
      makeConfig({ id: 2, symbol: '%', category: 'Percentage' }),
    ])

    expect(screen.getByText('$')).toBeInTheDocument()
    expect(screen.getByText('%')).toBeInTheDocument()
    // Category is displayed with localized label "Moneda ($)"
    expect(screen.getByText('Moneda ($)')).toBeInTheDocument()
  })

  it('displays "Global" when productTypeId is null', () => {
    renderTable([makeConfig({ productTypeId: null })])
    // Multiple "Global" texts may exist (table cell + select option) — assert at least one is present
    expect(screen.getAllByText('Global').length).toBeGreaterThanOrEqual(1)
  })

  it('shows "Vigente" badge for rows with validTo === null', () => {
    renderTable([makeConfig({ validTo: null, isActive: true })])
    expect(screen.getByText('Vigente')).toBeInTheDocument()
  })

  it('shows "Cerrada" badge for rows with validTo set', () => {
    renderTable([makeConfig({ validTo: '2026-03-31', isActive: false })])
    expect(screen.getByText('Cerrada')).toBeInTheDocument()
  })

  it('formats validFrom using formatCivilDate — shows dd/MM/yyyy', () => {
    renderTable([makeConfig({ validFrom: '2026-01-15' })])
    expect(screen.getByText('15/01/2026')).toBeInTheDocument()
  })

  // ── Conditional buttons ────────────────────────────────────────────────────

  it('active row shows Desactivar button but NOT Reactivar', () => {
    renderTable([makeConfig({ id: 1, isActive: true })])
    expect(screen.getByRole('button', { name: /desactivar/i })).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: /reactivar/i })).not.toBeInTheDocument()
  })

  it('inactive row shows Reactivar button but NOT Desactivar', () => {
    renderTable([makeConfig({ id: 1, isActive: false, validTo: '2026-03-31' })])
    expect(screen.getByRole('button', { name: /reactivar/i })).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: /desactivar/i })).not.toBeInTheDocument()
  })

  it('Eliminar button is visible for both active and inactive rows', () => {
    renderTable([
      makeConfig({ id: 1, isActive: true }),
      makeConfig({ id: 2, isActive: false, validTo: '2026-03-31' }),
    ])
    const eliminarBtns = screen.getAllByRole('button', { name: /eliminar/i })
    expect(eliminarBtns).toHaveLength(2)
  })

  it('clicking Desactivar calls onDeactivate with the correct config', async () => {
    const onDeactivate = vi.fn()
    const config = makeConfig({ id: 5, isActive: true, symbol: '€' })
    renderTable([config], vi.fn(), onDeactivate)

    await userEvent.click(screen.getByRole('button', { name: /desactivar/i }))
    expect(onDeactivate).toHaveBeenCalledWith(expect.objectContaining({ id: 5, symbol: '€' }))
  })

  it('clicking Reactivar calls PATCH /reactivate endpoint', async () => {
    const calls: unknown[] = []
    server.use(
      http.patch(`${API_URL}/api/v1/admin/chargeable-chars/7/reactivate`, () => {
        calls.push(true)
        return HttpResponse.json({ id: 7, symbol: '$', productTypeId: null, pricePerUnit: 1.5, validFrom: '2026-01-01' })
      }),
    )
    // Also mock the list invalidation re-fetch
    server.use(
      http.get(`${API_URL}/api/v1/admin/chargeable-chars`, () =>
        HttpResponse.json({ items: [], page: 1, pageSize: 20, total: 0 }),
      ),
    )

    const config = makeConfig({ id: 7, isActive: false, validTo: '2026-03-31' })
    renderTable([config])

    await userEvent.click(screen.getByRole('button', { name: /reactivar/i }))
    await waitFor(() => expect(calls.length).toBe(1), { timeout: 5000 })
  })
})
