// @vitest-environment node
import { describe, it, expect, vi } from 'vitest'
import {
  createPaginationParams,
  calculateOffset,
  calculateTotalPages,
  createPaginatedResult,
  countRecords,
  type PaginationParams
} from '../../server/database/queries/utils'

describe('Query Builder Utilities', () => {
  describe('createPaginationParams', () => {
    it('should return default values when query is empty', () => {
      const result = createPaginationParams({})
      expect(result).toEqual({ page: 1, pageSize: 20 })
    })

    it('should parse valid page and pageSize from query', () => {
      const result = createPaginationParams({ page: '2', pageSize: '50' })
      expect(result).toEqual({ page: 2, pageSize: 50 })
    })

    it('should handle numeric values in query', () => {
      const result = createPaginationParams({ page: 3, pageSize: 30 })
      expect(result).toEqual({ page: 3, pageSize: 30 })
    })

    it('should enforce minimum page of 1', () => {
      const result = createPaginationParams({ page: '0' })
      expect(result.page).toBe(1)
    })

    it('should enforce minimum page of 1 for negative values', () => {
      const result = createPaginationParams({ page: '-5' })
      expect(result.page).toBe(1)
    })

    it('should enforce minimum pageSize of 1', () => {
      const result = createPaginationParams({ pageSize: '0' })
      expect(result.pageSize).toBe(20) // Falls back to default
    })

    it('should enforce maximum pageSize', () => {
      const result = createPaginationParams({ pageSize: '200' })
      expect(result.pageSize).toBe(100) // Default max is 100
    })

    it('should use custom defaultPageSize', () => {
      const result = createPaginationParams({}, 10)
      expect(result.pageSize).toBe(10)
    })

    it('should use custom maxPageSize', () => {
      const result = createPaginationParams({ pageSize: '150' }, 20, 200)
      expect(result.pageSize).toBe(150)
    })

    it('should handle invalid string values', () => {
      const result = createPaginationParams({ page: 'invalid', pageSize: 'also-invalid' })
      expect(result).toEqual({ page: 1, pageSize: 20 })
    })

    it('should handle undefined values', () => {
      const result = createPaginationParams({ page: undefined, pageSize: undefined })
      expect(result).toEqual({ page: 1, pageSize: 20 })
    })
  })

  describe('calculateOffset', () => {
    it('should calculate offset for first page', () => {
      const params: PaginationParams = { page: 1, pageSize: 20 }
      expect(calculateOffset(params)).toBe(0)
    })

    it('should calculate offset for second page', () => {
      const params: PaginationParams = { page: 2, pageSize: 20 }
      expect(calculateOffset(params)).toBe(20)
    })

    it('should calculate offset for third page', () => {
      const params: PaginationParams = { page: 3, pageSize: 10 }
      expect(calculateOffset(params)).toBe(20)
    })

    it('should handle large page numbers', () => {
      const params: PaginationParams = { page: 10, pageSize: 50 }
      expect(calculateOffset(params)).toBe(450)
    })
  })

  describe('calculateTotalPages', () => {
    it('should calculate total pages correctly', () => {
      expect(calculateTotalPages(100, 20)).toBe(5)
    })

    it('should round up for partial pages', () => {
      expect(calculateTotalPages(101, 20)).toBe(6)
    })

    it('should return 1 for empty results', () => {
      expect(calculateTotalPages(0, 20)).toBe(0)
    })

    it('should handle single page', () => {
      expect(calculateTotalPages(10, 20)).toBe(1)
    })

    it('should handle exact division', () => {
      expect(calculateTotalPages(50, 10)).toBe(5)
    })
  })

  describe('createPaginatedResult', () => {
    it('should create paginated result with correct structure', () => {
      const data = [{ id: 1 }, { id: 2 }]
      const params: PaginationParams = { page: 1, pageSize: 20 }
      const result = createPaginatedResult(data, 100, params)

      expect(result).toEqual({
        data,
        total: 100,
        page: 1,
        pageSize: 20,
        totalPages: 5
      })
    })

    it('should calculate totalPages correctly', () => {
      const data = Array.from({ length: 10 }, (_, i) => ({ id: i }))
      const params: PaginationParams = { page: 2, pageSize: 10 }
      const result = createPaginatedResult(data, 25, params)

      expect(result.totalPages).toBe(3)
    })

    it('should handle empty data array', () => {
      const params: PaginationParams = { page: 1, pageSize: 20 }
      const result = createPaginatedResult([], 0, params)

      expect(result).toEqual({
        data: [],
        total: 0,
        page: 1,
        pageSize: 20,
        totalPages: 0
      })
    })

    it('should preserve data types', () => {
      const data = ['a', 'b', 'c']
      const params: PaginationParams = { page: 1, pageSize: 10 }
      const result = createPaginatedResult(data, 3, params)

      expect(result.data).toEqual(['a', 'b', 'c'])
    })
  })

  describe('countRecords', () => {
    it('should count records without where clause', async () => {
      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockResolvedValue([{ count: 42 }])
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table)

      expect(result).toBe(42)
      expect(mockDb.select).toHaveBeenCalledWith({ count: expect.anything() })
    })

    it('should count records with where clause', async () => {
      const mockWhereClause = { type: 'where' }

      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockReturnValue({
            where: vi.fn().mockResolvedValue([{ count: 10 }])
          })
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table, mockWhereClause)

      expect(result).toBe(10)
    })

    it('should handle zero count', async () => {
      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockResolvedValue([{ count: 0 }])
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table)

      expect(result).toBe(0)
    })

    it('should handle null count result', async () => {
      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockResolvedValue([{ count: null }])
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table)

      expect(result).toBe(0)
    })

    it('should handle empty result array', async () => {
      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockResolvedValue([])
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table)

      expect(result).toBe(0)
    })

    it('should handle string count values', async () => {
      const mockDb = {
        select: vi.fn().mockReturnValue({
          from: vi.fn().mockResolvedValue([{ count: '25' }])
        })
      }

      const table = {}
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const result = await countRecords(mockDb as any, table)

      expect(result).toBe(25)
    })
  })
})
