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 React from 'react'
import { ProductPriceHistory } from '../../../features/products/components/ProductPriceHistory'
import { useAuthStore } from '../../../stores/authStore'
import type { ProductPrice, PagedResult } from '../../../features/products/types'

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

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

// ─── Test data ────────────────────────────────────────────────────────────────

const mockActivePrice: ProductPrice = {
  id: 2,
  productId: 1,
  price: 1500.50,
  priceValidFrom: '2026-04-01',
  priceValidTo: null,
  isActive: true,
}

const mockClosedPrice: ProductPrice = {
  id: 1,
  productId: 1,
  price: 1000.00,
  priceValidFrom: '2026-01-01',
  priceValidTo: '2026-03-31',
  isActive: false,
}

const adminUser = {
  id: 1,
  username: 'admin',
  nombre: 'Admin',
  rol: 'admin',
  permisos: ['catalogo:productos:gestionar'],
  mustChangePassword: false,
}

const regularUser = {
  id: 2,
  username: 'viewer',
  nombre: 'Viewer',
  rol: 'viewer',
  permisos: [],
  mustChangePassword: false,
}

// ─── PagedResult helpers ──────────────────────────────────────────────────────

function makePagedResult(items: ProductPrice[], opts: { page?: number; pageSize?: number; total?: number } = {}): PagedResult<ProductPrice> {
  return {
    items,
    page: opts.page ?? 1,
    pageSize: opts.pageSize ?? 20,
    total: opts.total ?? items.length,
  }
}

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

const server = setupServer()

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

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

function renderHistory(productId = 1, user = adminUser) {
  useAuthStore.setState({ user })
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  return render(
    <QueryClientProvider client={qc}>
      <ProductPriceHistory productId={productId} />
    </QueryClientProvider>,
  )
}

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

describe('ProductPriceHistory — loading state', () => {
  it('renders skeleton while loading', () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, async () => {
        await new Promise(() => {})
        return HttpResponse.json(makePagedResult([]))
      }),
    )
    renderHistory()
    // Should show loading indicator
    const skeletons = document.querySelectorAll('[class*="skeleton"], .animate-pulse')
    expect(skeletons.length).toBeGreaterThan(0)
  })
})

describe('ProductPriceHistory — error state', () => {
  it('renders error message on fetch failure', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json({ error: 'server_error' }, { status: 500 }),
      ),
    )
    renderHistory()
    await waitFor(() =>
      expect(screen.getByText(/error al cargar precios/i)).toBeInTheDocument(),
    )
  })
})

describe('ProductPriceHistory — empty state', () => {
  it('shows CTA when no prices exist (total=0)', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([], { total: 0 })),
      ),
    )
    renderHistory()
    await waitFor(() =>
      expect(screen.getByText(/sin historial de precios/i)).toBeInTheDocument(),
    )
    // Should show at least one button to add first price (for users with permission)
    // Both header button and empty-state CTA render in empty state
    const addButtons = screen.getAllByRole('button', { name: /programar nuevo precio/i })
    expect(addButtons.length).toBeGreaterThan(0)
  })
})

describe('ProductPriceHistory — data rendering', () => {
  it('renders price list with formatted dates and prices', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice, mockClosedPrice])),
      ),
    )
    renderHistory()
    await waitFor(() =>
      expect(screen.getByText('01/04/2026')).toBeInTheDocument(),
    )
    // Active price row visible
    expect(screen.getByText('01/01/2026')).toBeInTheDocument()
    // Closed price "hasta" date visible
    expect(screen.getByText('31/03/2026')).toBeInTheDocument()
  })

  it('shows Badge "Vigente" for active price row (priceValidTo=null)', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice, mockClosedPrice])),
      ),
    )
    renderHistory()
    await waitFor(() => expect(screen.getByText('Vigente')).toBeInTheDocument())
  })

  it('shows formatted currency for prices', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice])),
      ),
    )
    renderHistory()
    // ARS currency format — 1500.50
    await waitFor(() => {
      const cells = screen.getAllByRole('cell')
      const hasCurrency = cells.some((c) => c.textContent?.includes('1.500') || c.textContent?.includes('1500'))
      expect(hasCurrency).toBe(true)
    })
  })
})

describe('ProductPriceHistory — dialog integration', () => {
  it('opens AddProductPriceDialog when "Programar nuevo precio" is clicked', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice])),
      ),
    )
    renderHistory()
    await waitFor(() => expect(screen.getByText('Vigente')).toBeInTheDocument())
    const btn = screen.getByRole('button', { name: /programar nuevo precio/i })
    await userEvent.click(btn)
    // Dialog should open — check for dialog heading
    await waitFor(() =>
      expect(screen.getByRole('dialog')).toBeInTheDocument(),
    )
  })

  it('hides "Programar nuevo precio" button when user lacks permission', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice])),
      ),
    )
    renderHistory(1, regularUser)
    await waitFor(() => expect(screen.getByText('Vigente')).toBeInTheDocument())
    expect(screen.queryByRole('button', { name: /programar nuevo precio/i })).not.toBeInTheDocument()
  })
})

// ─── §P.9 — §P.12: Pagination controls ───────────────────────────────────────

describe('ProductPriceHistory — pagination controls (§P.9–§P.12)', () => {
  // §P.9: page 1 of many — Next enabled, Previous disabled
  it('§P.9 — page 1: Next enabled and Previous disabled when total > pageSize', async () => {
    // 30 total, pageSize=20 → 2 pages
    const page1Items = Array.from({ length: 20 }, (_, i) => ({
      ...mockActivePrice,
      id: i + 1,
      isActive: i === 0,
    }))
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult(page1Items, { page: 1, pageSize: 20, total: 30 })),
      ),
    )
    renderHistory()
    await waitFor(() => expect(screen.getByRole('button', { name: /anterior/i })).toBeInTheDocument())

    const prevBtn = screen.getByRole('button', { name: /anterior/i })
    const nextBtn = screen.getByRole('button', { name: /siguiente/i })

    expect(prevBtn).toBeDisabled()
    expect(nextBtn).not.toBeDisabled()
  })

  // §P.10: click Next → refetch with page=2, renders new items
  it('§P.10 — click Next → refetches page=2 and shows new items', async () => {
    const price1 = { ...mockActivePrice, id: 1, price: 100, priceValidFrom: '2026-04-01', isActive: true }
    const price2 = { ...mockActivePrice, id: 21, price: 200, priceValidFrom: '2026-01-01', priceValidTo: '2026-03-31', isActive: false }

    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, ({ request }) => {
        const url = new URL(request.url)
        const page = url.searchParams.get('page') ?? '1'
        if (page === '2') {
          return HttpResponse.json(makePagedResult([price2], { page: 2, pageSize: 20, total: 21 }))
        }
        return HttpResponse.json(makePagedResult([price1], { page: 1, pageSize: 20, total: 21 }))
      }),
    )
    renderHistory()

    // Wait for page 1 to load — price1 has priceValidFrom 2026-04-01
    await waitFor(() => expect(screen.getByText('01/04/2026')).toBeInTheDocument())

    const nextBtn = screen.getByRole('button', { name: /siguiente/i })
    await userEvent.click(nextBtn)

    // Page 2 shows price2 — priceValidFrom 2026-01-01
    await waitFor(() => expect(screen.getByText('01/01/2026')).toBeInTheDocument())
  })

  // §P.11: first page always has Previous disabled
  it('§P.11 — primera página: botón Anterior siempre deshabilitado', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice], { page: 1, pageSize: 20, total: 1 })),
      ),
    )
    renderHistory()
    await waitFor(() => expect(screen.getByRole('button', { name: /anterior/i })).toBeInTheDocument())
    expect(screen.getByRole('button', { name: /anterior/i })).toBeDisabled()
  })

  // §P.12: last page has Next disabled (page * pageSize >= total)
  it('§P.12 — última página: botón Siguiente deshabilitado', async () => {
    // page=2, pageSize=20, total=21 → 2 pages, page 2 is last
    const lastPageItem = { ...mockClosedPrice, id: 21 }
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, ({ request }) => {
        const url = new URL(request.url)
        const page = url.searchParams.get('page') ?? '1'
        if (page === '2') {
          return HttpResponse.json(makePagedResult([lastPageItem], { page: 2, pageSize: 20, total: 21 }))
        }
        // Page 1: 20 items
        const items = Array.from({ length: 20 }, (_, i) => ({ ...mockClosedPrice, id: i + 1 }))
        return HttpResponse.json(makePagedResult(items, { page: 1, pageSize: 20, total: 21 }))
      }),
    )
    renderHistory()

    // Navigate to page 2
    await waitFor(() => expect(screen.getByRole('button', { name: /siguiente/i })).toBeInTheDocument())
    const nextBtn = screen.getByRole('button', { name: /siguiente/i })
    expect(nextBtn).not.toBeDisabled()

    await userEvent.click(nextBtn)

    // On page 2, Next should be disabled
    await waitFor(() => expect(screen.getByRole('button', { name: /siguiente/i })).toBeDisabled())
    expect(screen.getByRole('button', { name: /anterior/i })).not.toBeDisabled()
  })

  it('shows page info text', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () =>
        HttpResponse.json(makePagedResult([mockActivePrice], { page: 1, pageSize: 20, total: 30 })),
      ),
    )
    renderHistory()
    await waitFor(() =>
      expect(screen.getByText(/página 1 de 2/i)).toBeInTheDocument(),
    )
  })
})
