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 } 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,
}

// ─── 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([])
      }),
    )
    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', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/products/1/prices`, () => HttpResponse.json([])),
    )
    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([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([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([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([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([mockActivePrice]),
      ),
    )
    renderHistory(1, regularUser)
    await waitFor(() => expect(screen.getByText('Vigente')).toBeInTheDocument())
    expect(screen.queryByRole('button', { name: /programar nuevo precio/i })).not.toBeInTheDocument()
  })
})
