import '@angular/compiler';
import { Injector, runInInjectionContext } from '@angular/core';
import { Apollo } from 'apollo-angular';
import { gql } from 'graphql-tag';
import { of } from 'rxjs';
import { queryPaginate } from './query-paginate';

const MOCK_QUERY = gql`
  query PaginateTags($query: QueryStatement, $constraint: QueryStatement) {
    pagination: iamPaginateTags(query: $query, constraint: $constraint) {
      total
      count
      rows
    }
  }
`;

interface Tag {
  id: string;
  name: string;
}

describe('queryPaginate', () => {
  let apolloQuerySpy: ReturnType<typeof vi.fn>;
  let injector: Injector;

  beforeEach(() => {
    apolloQuerySpy = vi.fn().mockReturnValue(
      of({
        data: {
          pagination: { total: 1, count: 1, rows: [{ id: '1', name: 'Tag A' }] },
        },
      }),
    );

    injector = Injector.create({
      providers: [{ provide: Apollo, useValue: { query: apolloQuerySpy } }],
    });
  });

  function run(fields?: string, variables?: Record<string, unknown>) {
    return runInInjectionContext(injector, () =>
      queryPaginate<Tag>(MOCK_QUERY, 'pagination', fields, variables as never),
    );
  }

  describe('Apollo call', () => {
    it('calls apollo.query with the statement and variables', () => {
      run(undefined, { query: { offset: 0 } }).subscribe();
      expect(apolloQuerySpy).toHaveBeenCalledTimes(1);
      const arg = apolloQuerySpy.mock.calls[0][0];
      expect(arg.variables).toEqual({ query: { offset: 0 } });
    });

    it('passes headers into the context', () => {
      run(undefined, { headers: { 'X-Tenant-Id': 'abc' } }).subscribe();
      const arg = apolloQuerySpy.mock.calls[0][0];
      expect(arg.context).toEqual({ headers: { 'X-Tenant-Id': 'abc' } });
    });
  });

  describe('result mapping', () => {
    it('emits the paginated payload under the given key', () => {
      let result: unknown;
      run().subscribe((r) => (result = r));
      expect(result).toEqual({ total: 1, count: 1, rows: [{ id: '1', name: 'Tag A' }] });
    });

    it('emits an empty page when data is missing', () => {
      apolloQuerySpy.mockReturnValue(of({ data: null }));
      let result: unknown;
      run().subscribe((r) => (result = r));
      expect(result).toEqual({ count: 0, total: 0, rows: [] });
    });

    it('emits an empty page when the key is missing', () => {
      apolloQuerySpy.mockReturnValue(of({ data: {} }));
      let result: unknown;
      run().subscribe((r) => (result = r));
      expect(result).toEqual({ count: 0, total: 0, rows: [] });
    });
  });

  describe('purity', () => {
    it('does not hold state between calls', () => {
      run().subscribe();
      run().subscribe();
      expect(apolloQuerySpy).toHaveBeenCalledTimes(2);
    });
  });
});
