import { beforeEach, describe, expect, it, vi } from 'vitest'

import { fetchCurrentAuth, loginAuth, logoutAuth } from '@/services/authService'

const jsonHeaders = {
  headers: {
    get: (name: string) => (name.toLowerCase() === 'content-type' ? 'application/json' : null),
  },
}

function mockJsonResponse(payload: unknown, status = 200): Response {
  return {
    ok: status >= 200 && status < 300,
    status,
    json: async () => payload,
    text: async () => JSON.stringify(payload),
    ...jsonHeaders,
  } as Response
}

function createAuthPayload() {
  return {
    access_token: 'session-token',
    token_type: 'Bearer',
    expires_at: '2026-07-09T12:00:00Z',
    user: {
      id: '1900000000000000001',
      username: 'admin',
      display_name: '系统管理员',
      super_admin: true,
    },
    default_hotel_id: 'HOTEL-TEST',
    hotels: [
      {
        hotel_id: 'HOTEL-TEST',
        hotel_name: '测试酒店',
        time_zone: 'Asia/Bangkok',
        default_hotel: true,
      },
    ],
    permissions: ['RESERVATION_ORDER_READ'],
    menus: [
      {
        menu_code: 'RESERVATION_ORDERS',
        menu_name: '订单列表',
        route_path: '/reservation/orders',
        component_key: 'ReservationOrders',
        icon_key: 'pi pi-list',
        permission_code: 'RESERVATION_ORDER_READ',
        sort_order: 10,
      },
    ],
  }
}

describe('authService', () => {
  beforeEach(() => {
    vi.restoreAllMocks()
    sessionStorage.clear()
  })

  it('logs in with username, password, and optional preferred hotel id', async () => {
    const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse(createAuthPayload()))

    const result = await loginAuth({
      username: 'admin',
      password: 'Admin@123456',
      preferred_hotel_id: 'HOTEL-TEST',
    })

    expect(fetchMock).toHaveBeenCalledWith(
      '/api/auth/login',
      expect.objectContaining({
        method: 'POST',
        headers: expect.objectContaining({
          'Content-Type': 'application/json',
        }),
        body: JSON.stringify({
          username: 'admin',
          password: 'Admin@123456',
          preferred_hotel_id: 'HOTEL-TEST',
        }),
      }),
    )
    expect(result.access_token).toBe('session-token')
    expect(result.default_hotel_id).toBe('HOTEL-TEST')
  })

  it('restores the current user through auth me', async () => {
    const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse(createAuthPayload()))
    sessionStorage.setItem('th_hotel_access_token', 'session-token')

    const result = await fetchCurrentAuth()

    expect(fetchMock).toHaveBeenCalledWith(
      '/api/auth/me',
      expect.objectContaining({
        method: 'GET',
        headers: expect.objectContaining({
          Authorization: 'Bearer session-token',
        }),
      }),
    )
    expect(result.user.username).toBe('admin')
  })

  it('logs out the current token without persisting secrets outside sessionStorage', async () => {
    const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse({ success: true }))
    sessionStorage.setItem('th_hotel_access_token', 'session-token')

    const result = await logoutAuth()

    expect(fetchMock).toHaveBeenCalledWith(
      '/api/auth/logout',
      expect.objectContaining({
        method: 'POST',
        headers: expect.objectContaining({
          Authorization: 'Bearer session-token',
        }),
      }),
    )
    expect(result.success).toBe(true)
    expect(localStorage.getItem('th_hotel_access_token')).toBeNull()
  })
})
