import { describe, test, vi, expect, beforeEach, afterEach } from 'vitest'
import type { Query as PiniaOrmQuery } from 'pinia-orm'
import { ref } from 'vue'
import Query from '~/services/data/Query'
import type { ApiFilter } from '~/types/data'
import type ApiResource from '~/models/ApiResource'
import PageFilter from '~/services/data/Filters/PageFilter'
import EqualFilter from '~/services/data/Filters/EqualFilter'
import OrderBy from '~/services/data/Filters/OrderBy'

class TestableQuery extends Query {
  public getFilters() {
    return this.filters
  }
}

describe('constructor', () => {
  test('simple call', () => {
    // @ts-ignore
    const filter1 = vi.fn() as ApiFilter
    // @ts-ignore
    const filter2 = vi.fn() as ApiFilter

    const query = new TestableQuery(filter1, filter2)

    expect(query.getFilters()).toEqual([filter1, filter2])
  })
})

describe('add', () => {
  test('simple call', () => {
    // @ts-ignore
    const filter1 = vi.fn() as ApiFilter
    // @ts-ignore
    const filter2 = vi.fn() as ApiFilter

    const query = new TestableQuery(filter1)

    expect(query.getFilters()).toEqual([filter1])

    const result = query.add(filter2)

    expect(query.getFilters()).toEqual([filter1, filter2])

    expect(result).toEqual(query)
  })
})

describe('getUrlQuery', () => {
  test('simple call', () => {
    // @ts-ignore
    const filter1 = vi.fn() as ApiFilter
    // @ts-ignore
    const filter2 = vi.fn() as ApiFilter
    // @ts-ignore
    const filter3 = vi.fn() as ApiFilter

    const query = new TestableQuery(filter1, filter2, filter3)

    filter1.getApiQueryPart = vi.fn(() => 'foo=1')
    filter2.getApiQueryPart = vi.fn(() => '')
    filter3.getApiQueryPart = vi.fn(() => 'bar=2')

    const result = query.getUrlQuery()

    expect(result).toEqual('foo=1&bar=2')
  })
})

describe('applyToPiniaOrmQuery', () => {
  test('simple call', () => {
    const filter1 = new EqualFilter('a', 1)
    const filter2 = new EqualFilter('a', 1)
    const orderBy = new OrderBy('a')
    const pageFilter = new PageFilter(ref(1), ref(10))

    // @ts-ignore
    const piniaOrmQuery1 = vi.fn() as PiniaOrmQuery<ApiResource>
    // @ts-ignore
    const piniaOrmQuery2 = vi.fn() as PiniaOrmQuery<ApiResource>
    // @ts-ignore
    const piniaOrmQuery3 = vi.fn() as PiniaOrmQuery<ApiResource>
    // @ts-ignore
    const piniaOrmQuery4 = vi.fn() as PiniaOrmQuery<ApiResource>
    // @ts-ignore
    const piniaOrmQuery5 = vi.fn() as PiniaOrmQuery<ApiResource>

    const query = new TestableQuery(orderBy, filter1, pageFilter, filter2)

    filter1.applyToPiniaOrmQuery = vi.fn((query) => piniaOrmQuery2)
    filter2.applyToPiniaOrmQuery = vi.fn((query) => piniaOrmQuery3)
    orderBy.applyToPiniaOrmQuery = vi.fn((query) => piniaOrmQuery4)
    pageFilter.applyToPiniaOrmQuery = vi.fn((query) => piniaOrmQuery5)

    const result = query.applyToPiniaOrmQuery(piniaOrmQuery1)

    expect(filter1.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery1)
    expect(filter2.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery2)
    expect(orderBy.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery3)
    expect(pageFilter.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery4)

    expect(result).toEqual(piniaOrmQuery5)
  })
})
