import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { getRubroTree } from '../../../features/rubros/api/getRubroTree'
import { getRubroById } from '../../../features/rubros/api/getRubroById'
import { createRubro } from '../../../features/rubros/api/createRubro'
import { updateRubro } from '../../../features/rubros/api/updateRubro'
import { deleteRubro } from '../../../features/rubros/api/deleteRubro'
import { moveRubro } from '../../../features/rubros/api/moveRubro'
import type { RubroTreeNode, Rubro } from '../../../features/rubros/types'

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

const mockTree: RubroTreeNode[] = [
  {
    id: 1,
    nombre: 'Autos',
    orden: 1,
    activo: true,
    parentId: null,
    tarifarioBaseId: null,
    hijos: [
      {
        id: 2,
        nombre: 'Sedanes',
        orden: 1,
        activo: true,
        parentId: 1,
        tarifarioBaseId: null,
        hijos: [],
      },
    ],
  },
]

const mockRubro: Rubro = {
  id: 1,
  nombre: 'Autos',
  orden: 1,
  activo: true,
  parentId: null,
  tarifarioBaseId: null,
  fechaCreacion: '2026-04-18T00:00:00Z',
  fechaModificacion: null,
}

const server = setupServer()

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

describe('getRubroTree', () => {
  it('calls GET /api/v1/rubros/tree and returns tree', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, () => HttpResponse.json(mockTree)),
    )
    const result = await getRubroTree()
    expect(result).toEqual(mockTree)
  })

  it('passes incluirInactivos=true when requested', async () => {
    let capturedUrl = ''
    server.use(
      http.get(`${API_URL}/api/v1/rubros/tree`, ({ request }) => {
        capturedUrl = request.url
        return HttpResponse.json(mockTree)
      }),
    )
    await getRubroTree(true)
    expect(capturedUrl).toContain('incluirInactivos=true')
  })
})

describe('getRubroById', () => {
  it('calls GET /api/v1/rubros/:id and returns rubro', async () => {
    server.use(
      http.get(`${API_URL}/api/v1/rubros/1`, () => HttpResponse.json(mockRubro)),
    )
    const result = await getRubroById(1)
    expect(result).toEqual(mockRubro)
  })
})

describe('createRubro', () => {
  it('calls POST /api/v1/admin/rubros with payload', async () => {
    let capturedBody: unknown = null
    server.use(
      http.post(`${API_URL}/api/v1/admin/rubros`, async ({ request }) => {
        capturedBody = await request.json()
        return HttpResponse.json(mockRubro, { status: 201 })
      }),
    )
    const req = { nombre: 'Autos', parentId: null, tarifarioBaseId: null }
    await createRubro(req)
    expect(capturedBody).toEqual(req)
  })
})

describe('updateRubro', () => {
  it('calls PUT /api/v1/admin/rubros/:id with payload', async () => {
    let capturedBody: unknown = null
    server.use(
      http.put(`${API_URL}/api/v1/admin/rubros/1`, async ({ request }) => {
        capturedBody = await request.json()
        return HttpResponse.json(mockRubro)
      }),
    )
    const req = { nombre: 'Autos Actualizado', tarifarioBaseId: null }
    await updateRubro(1, req)
    expect(capturedBody).toEqual(req)
  })
})

describe('deleteRubro', () => {
  it('calls DELETE /api/v1/admin/rubros/:id', async () => {
    let called = false
    server.use(
      http.delete(`${API_URL}/api/v1/admin/rubros/1`, () => {
        called = true
        return new HttpResponse(null, { status: 204 })
      }),
    )
    await deleteRubro(1)
    expect(called).toBe(true)
  })
})

describe('moveRubro', () => {
  it('calls PATCH /api/v1/admin/rubros/:id/mover with payload', async () => {
    let capturedBody: unknown = null
    server.use(
      http.patch(`${API_URL}/api/v1/admin/rubros/1/mover`, async ({ request }) => {
        capturedBody = await request.json()
        return new HttpResponse(null, { status: 200 })
      }),
    )
    const req = { nuevoParentId: 2, nuevoOrden: 1 }
    await moveRubro(1, req)
    expect(capturedBody).toEqual(req)
  })
})
