/**
 * Tests for CostView component.
 *
 * Coverage:
 * 1. Loading state.
 * 2. Empty state.
 * 3. Renders costs table with data.
 * 4. Shows summary values.
 * 5. Recompute button triggers confirmation modal and then recompute call.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderWithProviders } from '../test-utils'

// ---------------------------------------------------------------------------
// Mock apiClient
// ---------------------------------------------------------------------------

const mockGet = vi.fn()
const mockPost = vi.fn()

vi.mock('../api/client', () => ({
  default: {
    GET: (...args: unknown[]) => mockGet(...args),
    POST: (...args: unknown[]) => mockPost(...args),
    PATCH: vi.fn(),
    DELETE: vi.fn(),
  },
  ApiError: class ApiError extends Error {
    status: number
    body: unknown
    constructor(status: number, body: unknown) {
      super(`API error ${status}`)
      this.name = 'ApiError'
      this.status = status
      this.body = body
    }
  },
  registerLoginRedirect: vi.fn(),
}))

// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------

const COST_PERIOD = {
  period_start: '2026-06-22T10:00:00Z',
  d1_kwh: 0.1,
  d2_kwh: 0.2,
  r1_kwh: 0.0,
  r2_kwh: 0.05,
  import_cost: 0.044,
  export_revenue: 0.005,
  net_cost: 0.039,
  currency: 'EUR',
  degraded: false,
  contract_version_id: 1,
}

const SUMMARY = {
  currency: 'EUR',
  metered_import: 10.5,
  metered_export: 2.3,
  metered_net: 8.2,
  fixed_costs: 5.0,
  credits: 50.0,
  total_payable: 12.5,
}

// ---------------------------------------------------------------------------
// Import component
// ---------------------------------------------------------------------------

import { CostView } from './CostView'

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

describe('CostView', () => {
  beforeEach(() => vi.clearAllMocks())

  it('renders loading state initially', () => {
    mockGet.mockImplementation(() => new Promise(() => {}))

    renderWithProviders(<CostView />)

    expect(screen.getByTestId('costs-loading')).toBeInTheDocument()
  })

  it('renders empty state when no cost periods available', async () => {
    mockGet.mockImplementation((path: string) => {
      if (path === '/api/energy/costs') {
        return Promise.resolve({ data: { items: [], total: 0 } })
      }
      if (path === '/api/energy/costs/summary') {
        return Promise.resolve({ data: SUMMARY })
      }
      return Promise.resolve({ data: null })
    })

    renderWithProviders(<CostView />)

    await waitFor(() => {
      expect(screen.getByTestId('costs-empty')).toBeInTheDocument()
    })
  })

  it('renders costs table when data is available', async () => {
    mockGet.mockImplementation((path: string) => {
      if (path === '/api/energy/costs') {
        return Promise.resolve({ data: { items: [COST_PERIOD], total: 1 } })
      }
      if (path === '/api/energy/costs/summary') {
        return Promise.resolve({ data: SUMMARY })
      }
      return Promise.resolve({ data: null })
    })

    renderWithProviders(<CostView />)

    await waitFor(() => {
      expect(screen.getByTestId('costs-table')).toBeInTheDocument()
    })

    expect(screen.getByTestId('cost-row-0')).toBeInTheDocument()
  })

  it('shows summary values correctly', async () => {
    mockGet.mockImplementation((path: string) => {
      if (path === '/api/energy/costs') {
        return Promise.resolve({ data: { items: [], total: 0 } })
      }
      if (path === '/api/energy/costs/summary') {
        return Promise.resolve({ data: SUMMARY })
      }
      return Promise.resolve({ data: null })
    })

    renderWithProviders(<CostView />)

    await waitFor(() => {
      expect(screen.getByTestId('summary-import')).toBeInTheDocument()
    })

    expect(screen.getByTestId('summary-import')).toHaveTextContent('10.500')
    expect(screen.getByTestId('summary-export')).toHaveTextContent('2.300')
    expect(screen.getByTestId('summary-total')).toHaveTextContent('12.50')
  })

  it('shows recompute confirmation modal on button click', async () => {
    const user = userEvent.setup()

    mockGet.mockImplementation((path: string) => {
      if (path === '/api/energy/costs') {
        return Promise.resolve({ data: { items: [], total: 0 } })
      }
      if (path === '/api/energy/costs/summary') {
        return Promise.resolve({ data: SUMMARY })
      }
      return Promise.resolve({ data: null })
    })

    renderWithProviders(<CostView />)

    await waitFor(() => {
      expect(screen.getByTestId('cost-recompute-button')).toBeInTheDocument()
    })

    await user.click(screen.getByTestId('cost-recompute-button'))

    await waitFor(() => {
      expect(screen.getByTestId('recompute-confirm-modal')).toBeInTheDocument()
    })
  })

  it('calls recompute mutation when confirmed', async () => {
    const user = userEvent.setup()

    mockGet.mockImplementation((path: string) => {
      if (path === '/api/energy/costs') {
        return Promise.resolve({ data: { items: [], total: 0 } })
      }
      if (path === '/api/energy/costs/summary') {
        return Promise.resolve({ data: SUMMARY })
      }
      return Promise.resolve({ data: null })
    })
    mockPost.mockResolvedValue({ data: { recomputed: 0 } })

    renderWithProviders(<CostView />)

    await waitFor(() => {
      expect(screen.getByTestId('cost-recompute-button')).toBeInTheDocument()
    })

    await user.click(screen.getByTestId('cost-recompute-button'))

    await waitFor(() => {
      expect(screen.getByTestId('recompute-confirm')).toBeInTheDocument()
    })

    await user.click(screen.getByTestId('recompute-confirm'))

    await waitFor(() => {
      expect(mockPost).toHaveBeenCalledWith(
        '/api/energy/costs/recompute',
        expect.any(Object),
      )
    })
  })
})
