import { describe, it } from 'mocha';

import { NoUnusedVariablesRule } from '../rules/NoUnusedVariablesRule.js';

import { expectValidationErrors } from './harness.js';

function expectErrors(queryStr: string) {
  return expectValidationErrors(NoUnusedVariablesRule, queryStr);
}

function expectValid(queryStr: string) {
  expectErrors(queryStr).toDeepEqual([]);
}

describe('Validate: No unused variables', () => {
  it('uses all variables', () => {
    expectValid(`
      query ($a: String, $b: String, $c: String) {
        field(a: $a, b: $b, c: $c)
      }
    `);
  });

  it('uses all variables deeply', () => {
    expectValid(`
      query Foo($a: String, $b: String, $c: String) {
        field(a: $a) {
          field(b: $b) {
            field(c: $c)
          }
        }
      }
    `);
  });

  it('uses all variables deeply in inline fragments', () => {
    expectValid(`
      query Foo($a: String, $b: String, $c: String) {
        ... on Type {
          field(a: $a) {
            field(b: $b) {
              ... on Type {
                field(c: $c)
              }
            }
          }
        }
      }
    `);
  });

  it('uses all variables in fragments', () => {
    expectValid(`
      query Foo($a: String, $b: String, $c: String) {
        ...FragA
      }
      fragment FragA on Type {
        field(a: $a) {
          ...FragB
        }
      }
      fragment FragB on Type {
        field(b: $b) {
          ...FragC
        }
      }
      fragment FragC on Type {
        field(c: $c)
      }
    `);
  });

  it('variable used by fragment in multiple operations', () => {
    expectValid(`
      query Foo($a: String) {
        ...FragA
      }
      query Bar($b: String) {
        ...FragB
      }
      fragment FragA on Type {
        field(a: $a)
      }
      fragment FragB on Type {
        field(b: $b)
      }
    `);
  });

  it('variable used by recursive fragment', () => {
    expectValid(`
      query Foo($a: String) {
        ...FragA
      }
      fragment FragA on Type {
        field(a: $a) {
          ...FragA
        }
      }
    `);
  });

  it('variable not used', () => {
    expectErrors(`
      query ($a: String, $b: String, $c: String) {
        field(a: $a, b: $b)
      }
    `).toDeepEqual([
      {
        message: 'Variable "$c" is never used.',
        locations: [{ line: 2, column: 38 }],
      },
    ]);
  });

  it('multiple variables not used', () => {
    expectErrors(`
      query Foo($a: String, $b: String, $c: String) {
        field(b: $b)
      }
    `).toDeepEqual([
      {
        message: 'Variable "$a" is never used in operation "Foo".',
        locations: [{ line: 2, column: 17 }],
      },
      {
        message: 'Variable "$c" is never used in operation "Foo".',
        locations: [{ line: 2, column: 41 }],
      },
    ]);
  });

  it('variable not used in fragments', () => {
    expectErrors(`
      query Foo($a: String, $b: String, $c: String) {
        ...FragA
      }
      fragment FragA on Type {
        field(a: $a) {
          ...FragB
        }
      }
      fragment FragB on Type {
        field(b: $b) {
          ...FragC
        }
      }
      fragment FragC on Type {
        field
      }
    `).toDeepEqual([
      {
        message: 'Variable "$c" is never used in operation "Foo".',
        locations: [{ line: 2, column: 41 }],
      },
    ]);
  });

  it('multiple variables not used in fragments', () => {
    expectErrors(`
      query Foo($a: String, $b: String, $c: String) {
        ...FragA
      }
      fragment FragA on Type {
        field {
          ...FragB
        }
      }
      fragment FragB on Type {
        field(b: $b) {
          ...FragC
        }
      }
      fragment FragC on Type {
        field
      }
    `).toDeepEqual([
      {
        message: 'Variable "$a" is never used in operation "Foo".',
        locations: [{ line: 2, column: 17 }],
      },
      {
        message: 'Variable "$c" is never used in operation "Foo".',
        locations: [{ line: 2, column: 41 }],
      },
    ]);
  });

  it('variable not used by unreferenced fragment', () => {
    expectErrors(`
      query Foo($b: String) {
        ...FragA
      }
      fragment FragA on Type {
        field(a: $a)
      }
      fragment FragB on Type {
        field(b: $b)
      }
    `).toDeepEqual([
      {
        message: 'Variable "$b" is never used in operation "Foo".',
        locations: [{ line: 2, column: 17 }],
      },
    ]);
  });

  it('variable not used by fragment used by other operation', () => {
    expectErrors(`
      query Foo($b: String) {
        ...FragA
      }
      query Bar($a: String) {
        ...FragB
      }
      fragment FragA on Type {
        field(a: $a)
      }
      fragment FragB on Type {
        field(b: $b)
      }
    `).toDeepEqual([
      {
        message: 'Variable "$b" is never used in operation "Foo".',
        locations: [{ line: 2, column: 17 }],
      },
      {
        message: 'Variable "$a" is never used in operation "Bar".',
        locations: [{ line: 5, column: 17 }],
      },
    ]);
  });
});