/**
 * Tests for energy hooks in hooks.ts — energy pricing API wrappers.
 *
 * Coverage:
 * 1. useContracts — GET /api/energy/contracts
 * 2. useCreateContract — POST /api/energy/contracts
 * 3. useEnergyPrices — GET /api/energy/prices
 * 4. useEnergyCosts — GET /api/energy/costs
 * 5. useEnergyCostSummary — GET /api/energy/costs/summary
 * 6. useRecomputeCosts — POST /api/energy/costs/recompute
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, waitFor, act } from '@testing-library/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import type { ReactNode } from 'react'

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

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

vi.mock('../api/client', () => ({
  default: {
    GET: (...args: unknown[]) => mockGet(...args),
    POST: (...args: unknown[]) => mockPost(...args),
    PATCH: (...args: unknown[]) => mockPatch(...args),
    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 CONTRACT = {
  id: 1,
  name: 'Test Contract',
  kind: 'manual',
  active: true,
  currency: 'EUR',
  created_at: '2026-06-01T00:00:00Z',
  updated_at: '2026-06-01T00:00:00Z',
}

const PRICE_POINT = {
  starts_at: '2026-06-22T10:00:00Z',
  buy: 0.133,
  sell: 0.09,
  level: 'NORMAL',
}

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

// ---------------------------------------------------------------------------
// Wrapper
// ---------------------------------------------------------------------------

function makeWrapper() {
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  })
  function Wrapper({ children }: { children: ReactNode }) {
    return <QueryClientProvider client={qc}>{children}</QueryClientProvider>
  }
  return { qc, Wrapper }
}

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

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

  it('calls GET /api/energy/contracts and returns contract list', async () => {
    mockGet.mockResolvedValue({ data: { items: [CONTRACT], total: 1 } })

    const { Wrapper } = makeWrapper()
    const { useContracts } = await import('./hooks')
    const { result } = renderHook(() => useContracts(), { wrapper: Wrapper })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(mockGet).toHaveBeenCalledWith('/api/energy/contracts')
    expect(result.current.data?.items).toHaveLength(1)
    expect(result.current.data?.items[0].id).toBe(1)
  })
})

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

  it('calls POST /api/energy/contracts with the provided body', async () => {
    mockPost.mockResolvedValue({ data: { ...CONTRACT, versions: [] } })
    mockGet.mockResolvedValue({ data: { items: [], total: 0 } })

    const { Wrapper } = makeWrapper()
    const { useCreateContract } = await import('./hooks')
    const { result } = renderHook(() => useCreateContract(), { wrapper: Wrapper })

    const body = {
      name: 'New Contract',
      kind: 'manual',
      currency: 'EUR',
      values: { energy: { buy: { normal: 0.133 } } },
    }

    await act(async () => {
      await result.current.mutateAsync(body)
    })

    expect(mockPost).toHaveBeenCalledWith('/api/energy/contracts', { body })
  })
})

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

  it('calls GET /api/energy/prices with start and end params', async () => {
    mockGet.mockResolvedValue({
      data: {
        kind: 'tibber',
        currency: 'EUR',
        points: [PRICE_POINT],
        tariff: null,
      },
    })

    const { Wrapper } = makeWrapper()
    const { useEnergyPrices } = await import('./hooks')
    const start = '2026-06-22T00:00:00Z'
    const end = '2026-06-23T00:00:00Z'
    const { result } = renderHook(() => useEnergyPrices(start, end), { wrapper: Wrapper })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(mockGet).toHaveBeenCalledWith('/api/energy/prices', {
      params: { query: { start, end } },
    })
    expect(result.current.data?.points).toHaveLength(1)
    expect(result.current.data?.kind).toBe('tibber')
  })

  it('calls GET /api/energy/prices without params when none provided', async () => {
    mockGet.mockResolvedValue({
      data: { kind: 'manual', currency: 'EUR', points: [], tariff: null },
    })

    const { Wrapper } = makeWrapper()
    const { useEnergyPrices } = await import('./hooks')
    const { result } = renderHook(() => useEnergyPrices(), { wrapper: Wrapper })

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(mockGet).toHaveBeenCalledWith('/api/energy/prices', {
      params: { query: {} },
    })
  })
})

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

  it('calls GET /api/energy/costs with start, end, and limit', async () => {
    mockGet.mockResolvedValue({
      data: { items: [COST_PERIOD], total: 1 },
    })

    const { Wrapper } = makeWrapper()
    const { useEnergyCosts } = await import('./hooks')
    const start = '2026-06-22T00:00:00Z'
    const end = '2026-06-23T00:00:00Z'
    const limit = 100
    const { result } = renderHook(
      () => useEnergyCosts(start, end, limit),
      { wrapper: Wrapper },
    )

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(mockGet).toHaveBeenCalledWith('/api/energy/costs', {
      params: { query: { start, end, limit } },
    })
    expect(result.current.data?.items).toHaveLength(1)
  })
})

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

  it('calls GET /api/energy/costs/summary with date range', async () => {
    mockGet.mockResolvedValue({ data: SUMMARY })

    const { Wrapper } = makeWrapper()
    const { useEnergyCostSummary } = await import('./hooks')
    const start = '2026-06-01T00:00:00Z'
    const end = '2026-06-30T23:59:59Z'
    const { result } = renderHook(
      () => useEnergyCostSummary(start, end),
      { wrapper: Wrapper },
    )

    await waitFor(() => expect(result.current.isSuccess).toBe(true))

    expect(mockGet).toHaveBeenCalledWith('/api/energy/costs/summary', {
      params: { query: { start, end } },
    })
    expect(result.current.data?.total_payable).toBe(12.5)
  })
})

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

  it('calls POST /api/energy/costs/recompute and invalidates cost queries', async () => {
    mockPost.mockResolvedValue({ data: { recomputed: 10 } })
    // Mock GET for invalidation queries (costs and summary)
    mockGet.mockResolvedValue({ data: { items: [], total: 0 } })

    const { Wrapper } = makeWrapper()
    const { useRecomputeCosts } = await import('./hooks')
    const { result } = renderHook(() => useRecomputeCosts(), { wrapper: Wrapper })

    await act(async () => {
      await result.current.mutateAsync({
        start: '2026-06-22T00:00:00Z',
        end: '2026-06-23T00:00:00Z',
      })
    })

    expect(mockPost).toHaveBeenCalledWith('/api/energy/costs/recompute', {
      params: {
        query: {
          start: '2026-06-22T00:00:00Z',
          end: '2026-06-23T00:00:00Z',
        },
      },
    })
  })

  it('calls POST /api/energy/costs/recompute without params when none provided', async () => {
    mockPost.mockResolvedValue({ data: { recomputed: 0 } })

    const { Wrapper } = makeWrapper()
    const { useRecomputeCosts } = await import('./hooks')
    const { result } = renderHook(() => useRecomputeCosts(), { wrapper: Wrapper })

    await act(async () => {
      await result.current.mutateAsync({})
    })

    expect(mockPost).toHaveBeenCalledWith('/api/energy/costs/recompute', {
      params: { query: {} },
    })
  })
})
