/**
 * @fileoverview Tests for GraphQL Query Builder
 */

import { describe, it, expect } from 'vitest';
import { GraphQLQueryBuilder } from '../query-builder.js';
import { asProjectKey, asRunId } from '../../../types/branded.js';

describe('GraphQLQueryBuilder', () => {
  describe('Basic Query Building', () => {
    it('should build a simple query', () => {
      const builder = new GraphQLQueryBuilder();
      const { query, variables } = builder.select('id').select('name').build();

      expect(query).toBe('query {\n  id\n  name\n}');
      expect(variables).toEqual({});
    });

    it('should build a named query', () => {
      const builder = new GraphQLQueryBuilder();
      const { query } = builder.withName('GetUser').select('id').select('name').build();

      expect(query).toBe('query GetUser {\n  id\n  name\n}');
    });

    it('should build a mutation', () => {
      const builder = new GraphQLQueryBuilder('mutation');
      const { query } = builder.withName('UpdateUser').select('id').build();

      expect(query).toBe('mutation UpdateUser {\n  id\n}');
    });
  });

  describe('Variables', () => {
    it('should handle variables', () => {
      const builder = new GraphQLQueryBuilder();
      const { query, variables } = builder
        .withName('GetUser')
        .withVariable('id', 'ID!', '123')
        .selectNested('user', ['id', 'name'], { id: '$id' })
        .build();

      expect(query).toContain('query GetUser($id: ID!)');
      expect(query).toContain('user(id: $id)');
      expect(variables).toEqual({ id: '123' });
    });

    it('should handle multiple variables', () => {
      const builder = new GraphQLQueryBuilder();
      const { query, variables } = builder
        .withVariable('first', 'Int', 10)
        .withVariable('after', 'String', 'cursor123')
        .selectNested('items', ['id'], { first: '$first', after: '$after' })
        .build();

      expect(query).toContain('$first: Int, $after: String');
      expect(variables).toEqual({ first: 10, after: 'cursor123' });
    });
  });

  describe('Nested Fields', () => {
    it('should handle nested field selections', () => {
      const builder = new GraphQLQueryBuilder();
      const { query } = builder
        .selectNested('user', [
          'id',
          'name',
          {
            name: 'profile',
            fields: [{ name: 'bio' }, { name: 'avatar' }],
          },
        ])
        .build();

      expect(query).toContain('user {');
      expect(query).toContain('id');
      expect(query).toContain('name');
      expect(query).toContain('profile {');
      expect(query).toContain('bio');
      expect(query).toContain('avatar');
    });

    it('should handle deeply nested fields', () => {
      const builder = new GraphQLQueryBuilder();
      const { query } = builder
        .selectNested('repository', [
          'id',
          {
            name: 'issues',
            args: { first: 10 },
            fields: [
              {
                name: 'edges',
                fields: [
                  {
                    name: 'node',
                    fields: [{ name: 'id' }, { name: 'title' }],
                  },
                ],
              },
            ],
          },
        ])
        .build();

      expect(query).toContain('repository {');
      expect(query).toContain('issues(first: 10) {');
      expect(query).toContain('edges {');
      expect(query).toContain('node {');
      expect(query).toContain('title');
    });
  });

  describe('Field Aliases', () => {
    it('should handle field aliases', () => {
      const builder = new GraphQLQueryBuilder();
      const { query } = builder
        .select({ name: 'id', alias: 'userId' })
        .select({ name: 'name', alias: 'userName' })
        .build();

      expect(query).toContain('userId: id');
      expect(query).toContain('userName: name');
    });
  });

  describe('Fragments', () => {
    it('should handle fragment definitions and usage', () => {
      const fragment = {
        name: 'UserFields',
        type: 'User',
        fields: [{ name: 'id' }, { name: 'name' }, { name: 'email' }],
      };

      const builder = new GraphQLQueryBuilder();
      const { query } = builder
        .withFragment(fragment)
        .selectNested('user', [])
        .useFragment('UserFields')
        .build();

      expect(query).toContain('fragment UserFields on User {');
      expect(query).toContain('...UserFields');
    });
  });

  describe('selectMany', () => {
    it('should handle multiple selections at once', () => {
      const builder = new GraphQLQueryBuilder();
      const { query } = builder
        .selectMany('id', 'name', 'email', { name: 'age', alias: 'userAge' })
        .build();

      expect(query).toContain('id');
      expect(query).toContain('name');
      expect(query).toContain('email');
      expect(query).toContain('userAge: age');
    });
  });

  describe('Static Factory Methods', () => {
    it('should create a projects query', () => {
      const { query } = GraphQLQueryBuilder.projects().build();

      expect(query).toContain('query GetProjects');
      expect(query).toContain('viewer {');
      expect(query).toContain('accounts {');
      expect(query).toContain('repositories {');
    });

    it('should create a run query', () => {
      const projectKey = asProjectKey('test-project');
      const runId = asRunId('run-123');
      const { query, variables } = GraphQLQueryBuilder.run(projectKey, runId).build();

      expect(query).toContain('query GetRun($projectKey: String!, $runId: ID!)');
      expect(query).toContain('run(projectKey: $projectKey, runUid: $runId)');
      expect(variables).toEqual({
        projectKey: 'test-project',
        runId: 'run-123',
      });
    });

    it('should create a quality metrics query', () => {
      const projectKey = asProjectKey('test-project');
      const { query, variables } = GraphQLQueryBuilder.qualityMetrics(projectKey).build();

      expect(query).toContain('query GetQualityMetrics($projectKey: String!)');
      expect(query).toContain('repository(key: $projectKey)');
      expect(query).toContain('metrics {');
      expect(query).toContain('items {');
      expect(variables).toEqual({ projectKey: 'test-project' });
    });
  });

  describe('Fragments Factory Methods', () => {
    it('should create issue fields fragment', () => {
      const fragment = GraphQLQueryBuilder.issueFieldsFragment();

      expect(fragment.name).toBe('IssueFields');
      expect(fragment.type).toBe('Issue');
      expect(fragment.fields).toContainEqual({ name: 'id' });
      expect(fragment.fields).toContainEqual({ name: 'title' });
      expect(fragment.fields).toContainEqual({ name: 'issueCode' });
    });

    it('should create run fields fragment', () => {
      const fragment = GraphQLQueryBuilder.runFieldsFragment();

      expect(fragment.name).toBe('RunFields');
      expect(fragment.type).toBe('Run');
      expect(fragment.fields).toContainEqual({ name: 'runUid' });
      expect(fragment.fields).toContainEqual({ name: 'commitOid' });
      expect(fragment.fields).toContainEqual({ name: 'status' });
    });
  });

  describe('Complex Query Example', () => {
    it('should build a complex query with all features', () => {
      const issueFragment = GraphQLQueryBuilder.issueFieldsFragment();

      const builder = new GraphQLQueryBuilder();
      const { query, variables } = builder
        .withName('GetRepositoryData')
        .withVariable('projectKey', 'String!', 'my-project')
        .withVariable('first', 'Int', 50)
        .withFragment(issueFragment)
        .selectNested(
          'repository',
          [
            'id',
            'name',
            {
              name: 'issues',
              args: { first: '$first' },
              fields: [
                {
                  name: 'edges',
                  fields: [
                    {
                      name: 'node',
                      fields: [{ name: '...IssueFields' }],
                    },
                  ],
                },
                {
                  name: 'pageInfo',
                  fields: [{ name: 'hasNextPage' }, { name: 'endCursor' }],
                },
              ],
            },
          ],
          { key: '$projectKey' }
        )
        .build();

      expect(query).toContain('query GetRepositoryData($projectKey: String!, $first: Int)');
      expect(query).toContain('repository(key: $projectKey)');
      expect(query).toContain('issues(first: $first)');
      expect(query).toContain('...IssueFields');
      expect(query).toContain('fragment IssueFields on Issue');
      expect(variables).toEqual({
        projectKey: 'my-project',
        first: 50,
      });
    });
  });
});
