import { describe, it, expect, beforeAll, afterAll, afterEach, vi, beforeEach } 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 { AddProductPriceDialog } from '../../../features/products/components/AddProductPriceDialog'
import type { AddProductPriceResponse } from '../../../features/products/types'

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

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

// ─── Server ───────────────────────────────────────────────────────────────────

const server = setupServer()

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

// ─── Fake timers helper ───────────────────────────────────────────────────────
// Fix "today" to 2026-04-19 ART (UTC-3).
// 2026-04-19T12:00:00-03:00 = 2026-04-19T15:00:00Z
// todayArgentina() uses Intl.DateTimeFormat with timeZone so this is stable.

function setupFakeTimers() {
  vi.useFakeTimers({ shouldAdvanceTime: true })
  vi.setSystemTime(new Date('2026-04-19T15:00:00.000Z'))
}

// ─── Render helper ────────────────────────────────────────────────────────────

function renderDialog(onOpenChange = vi.fn()) {
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  return {
    qc,
    result: render(
      <QueryClientProvider client={qc}>
        <AddProductPriceDialog
          open={true}
          onOpenChange={onOpenChange}
          productId={1}
        />
      </QueryClientProvider>,
    ),
  }
}

// ─── Tests ────────────────────────────────────────────────────────────────────

describe('AddProductPriceDialog — renders', () => {
  it('renders dialog with price and date fields', () => {
    renderDialog()
    expect(screen.getByRole('dialog')).toBeInTheDocument()
    // Use role spinbutton (number input) for price field
    expect(screen.getByRole('spinbutton', { name: /precio$/i })).toBeInTheDocument()
    expect(screen.getByLabelText(/vigente desde/i)).toBeInTheDocument()
  })
})

describe('AddProductPriceDialog — client-side validation', () => {
  beforeEach(() => {
    setupFakeTimers()
  })

  it('shows error when priceValidFrom is yesterday (2026-04-18 < 2026-04-19)', async () => {
    renderDialog()

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '100')

    // "Yesterday" in ART = 2026-04-18
    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-18')

    const submitBtn = screen.getByRole('button', { name: /^guardar$/i })
    await userEvent.click(submitBtn)

    await waitFor(
      () => expect(screen.getByText(/no puede ser anterior a hoy/i)).toBeInTheDocument(),
      { timeout: 3000 },
    )
  })

  it('accepts priceValidFrom = today (2026-04-19) as valid — no date error shown', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/admin/products/1/prices`, () =>
        HttpResponse.json(
          {
            created: { id: 1, productId: 1, price: 100, priceValidFrom: '2026-04-19', priceValidTo: null, isActive: true },
            closed: null,
          } satisfies AddProductPriceResponse,
          { status: 201 },
        ),
      ),
    )

    const onOpenChange = vi.fn()
    renderDialog(onOpenChange)

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '100')

    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-19')

    const submitBtn = screen.getByRole('button', { name: /^guardar$/i })
    await userEvent.click(submitBtn)

    // Should NOT show the date validation error
    await waitFor(
      () => expect(screen.queryByText(/no puede ser anterior a hoy/i)).not.toBeInTheDocument(),
      { timeout: 3000 },
    )
  })

  it('shows error when price is 0', async () => {
    renderDialog()

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '0')

    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-25')

    const submitBtn = screen.getByRole('button', { name: /^guardar$/i })
    await userEvent.click(submitBtn)

    await waitFor(
      () => expect(screen.getByText(/debe ser mayor/i)).toBeInTheDocument(),
      { timeout: 3000 },
    )
  })

  it('shows error when price is negative', async () => {
    renderDialog()

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '-50')

    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-25')

    const submitBtn = screen.getByRole('button', { name: /^guardar$/i })
    await userEvent.click(submitBtn)

    await waitFor(
      () => expect(screen.getByText(/debe ser mayor/i)).toBeInTheDocument(),
      { timeout: 3000 },
    )
  })
})

describe('AddProductPriceDialog — happy path submit', () => {
  beforeEach(() => {
    setupFakeTimers()
  })

  it('calls mutation with correct payload and closes on success', async () => {
    const mockResponse: AddProductPriceResponse = {
      created: {
        id: 2,
        productId: 1,
        price: 500.25,
        priceValidFrom: '2026-04-25',
        priceValidTo: null,
        isActive: true,
      },
      closed: null,
    }

    let capturedBody: unknown = null
    server.use(
      http.post(`${API_URL}/api/v1/admin/products/1/prices`, async ({ request }) => {
        capturedBody = await request.json()
        return HttpResponse.json(mockResponse, { status: 201 })
      }),
    )

    const onOpenChange = vi.fn()
    renderDialog(onOpenChange)

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '500.25')

    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-25')

    await userEvent.click(screen.getByRole('button', { name: /^guardar$/i }))

    await waitFor(
      () => expect(capturedBody).toEqual({ price: 500.25, priceValidFrom: '2026-04-25' }),
      { timeout: 3000 },
    )
    await waitFor(() => expect(onOpenChange).toHaveBeenCalledWith(false), { timeout: 3000 })
  })
})

describe('AddProductPriceDialog — server error 409', () => {
  beforeEach(() => {
    setupFakeTimers()
  })

  it('shows inline message when server returns 409 (ForwardOnly)', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/admin/products/1/prices`, () =>
        HttpResponse.json(
          {
            error: 'product_price_forward_only',
            message: 'La fecha debe ser posterior al precio vigente',
          },
          { status: 409 },
        ),
      ),
    )
    renderDialog()

    const priceInput = screen.getByRole('spinbutton', { name: /precio$/i })
    await userEvent.clear(priceInput)
    await userEvent.type(priceInput, '100')

    const dateInput = screen.getByLabelText(/vigente desde/i)
    await userEvent.clear(dateInput)
    await userEvent.type(dateInput, '2026-04-25')

    await userEvent.click(screen.getByRole('button', { name: /^guardar$/i }))

    await waitFor(
      () => expect(screen.getByText(/fecha debe ser posterior/i)).toBeInTheDocument(),
      { timeout: 3000 },
    )
  })
})
