import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest'
import { http, HttpResponse } from 'msw'
import { setupServer } from 'msw/node'
import { login, refresh, logout } from '../../../features/auth/api/authApi'

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

const mockLoginResponse = {
  accessToken: 'eyJhbGciOiJSUzI1NiJ9.payload.signature',
  refreshToken: 'opaque-refresh-token-abc123',
  expiresIn: 3600,
  usuario: {
    id: 1,
    username: 'admin',
    nombre: 'Admin',
    rol: 'admin',
    permisos: ['administracion:usuarios:gestionar'],
  },
}

const mockRefreshResponse = {
  accessToken: 'eyJhbGciOiJSUzI1NiJ9.new-payload.new-sig',
  refreshToken: 'new-opaque-refresh-token-xyz',
  expiresIn: 3600,
}

const mockLogoutResponse = {
  success: true,
  mensaje: 'Sesion cerrada correctamente',
}

const server = setupServer(
  http.post(`${API_URL}/api/v1/auth/login`, async ({ request }) => {
    const body = await request.json() as { username: string; password: string }
    if (body.username === 'admin' && body.password === '@Diego550@') {
      return HttpResponse.json(mockLoginResponse, { status: 200 })
    }
    return HttpResponse.json({ error: 'Credenciales inválidas' }, { status: 401 })
  }),

  http.post(`${API_URL}/api/v1/auth/refresh`, async ({ request }) => {
    const body = await request.json() as { accessToken: string; refreshToken: string }
    if (body.accessToken && body.refreshToken) {
      return HttpResponse.json(mockRefreshResponse, { status: 200 })
    }
    return HttpResponse.json({ error: 'invalid_token' }, { status: 401 })
  }),

  http.post(`${API_URL}/api/v1/auth/logout`, async () => {
    return HttpResponse.json(mockLogoutResponse, { status: 200 })
  }),
)

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

describe('login()', () => {
  it('returns auth data on valid credentials', async () => {
    const result = await login('admin', '@Diego550@')

    expect(result.accessToken).toBe(mockLoginResponse.accessToken)
    expect(result.refreshToken).toBe(mockLoginResponse.refreshToken)
    expect(result.expiresIn).toBe(3600)
    expect(result.usuario.username).toBe('admin')
  })

  it('throws on invalid credentials (401)', async () => {
    await expect(login('admin', 'wrongpassword')).rejects.toThrow()
  })
})

describe('refresh()', () => {
  it('refresh_callsCorrectEndpoint_withPayload', async () => {
    let capturedBody: unknown = null
    server.use(
      http.post(`${API_URL}/api/v1/auth/refresh`, async ({ request }) => {
        capturedBody = await request.json()
        return HttpResponse.json(mockRefreshResponse, { status: 200 })
      }),
    )

    const payload = {
      accessToken: 'old-access-token',
      refreshToken: 'old-refresh-token',
    }
    const result = await refresh(payload)

    expect(result.accessToken).toBe(mockRefreshResponse.accessToken)
    expect(result.refreshToken).toBe(mockRefreshResponse.refreshToken)
    expect(result.expiresIn).toBe(3600)
    expect(capturedBody).toEqual(payload)
  })

  it('throws on invalid refresh token (401)', async () => {
    server.use(
      http.post(`${API_URL}/api/v1/auth/refresh`, () => {
        return HttpResponse.json({ error: 'invalid_token' }, { status: 401 })
      }),
    )

    await expect(
      refresh({ accessToken: 'bad-access', refreshToken: 'bad-refresh' }),
    ).rejects.toThrow()
  })
})

describe('logout()', () => {
  it('logout_callsCorrectEndpoint', async () => {
    let requestUrl: string | null = null
    let requestMethod: string | null = null

    server.use(
      http.post(`${API_URL}/api/v1/auth/logout`, ({ request }) => {
        requestUrl = request.url
        requestMethod = request.method
        return HttpResponse.json(mockLogoutResponse, { status: 200 })
      }),
    )

    const result = await logout()

    expect(result.success).toBe(true)
    expect(result.mensaje).toBe('Sesion cerrada correctamente')
    expect(requestUrl).toContain('/api/v1/auth/logout')
    expect(requestMethod).toBe('POST')
  })
})
