import 'package:sqlparser/sqlparser.dart';
import 'package:test/test.dart';

import 'data.dart';

void main() {
  test('resolves columns from CTEs', () {
    final engine = SqlEngine()..registerTable(demoTable);

    final context = engine.analyze('''
      WITH
        cte (foo, bar) AS (SELECT * FROM demo)
      SELECT * FROM cte;
    ''');

    expect(context.errors, isEmpty);
    final select = context.root as SelectStatement;

    expect(select.resolvedColumns!.map((c) => c.name), ['foo', 'bar']);
    expect(
      select.resolvedColumns!.map((c) => context.typeOf(c).type),
      [id.type, content.type],
    );
  });

  test('warns on column count mismatch', () {
    final engine = SqlEngine()..registerTable(demoTable);

    final context = engine.analyze('''
      WITH
        cte (foo, bar, baz) AS (SELECT * FROM demo)
      SELECT 1;
    ''');

    expect(context.errors, hasLength(1));
    final error = context.errors.single;
    expect(error.type, AnalysisErrorType.cteColumnCountMismatch);
    expect(error.message, stringContainsInOrder(['3', '2']));
  });

  test('handles recursive CTEs', () {
    final engine = SqlEngine();

    final context = engine.analyze('''
      WITH RECURSIVE
        cnt(x) AS (
          SELECT 1
          UNION ALL
          SELECT x+1 FROM cnt
            LIMIT 1000000
        )
      SELECT x FROM cnt;
    ''');

    expect(context.errors, isEmpty);
    final select = context.root as SelectStatement;
    final column = select.resolvedColumns!.single;
    final type = context.typeOf(column);

    expect(type.type, const ResolvedType(type: BasicType.int));
    expect(column.name, 'x');
  });

  test('column references into CTE are case-insensitive', () async {
    final engine = SqlEngine()..registerTable(demoTable);
    final result = engine.analyze('''
  WITH shuffle_order AS (
    SELECT ROWID, row_number() OVER (ORDER BY RANDOM()) rn
    FROM demo
  )
  UPDATE demo SET
    content = (SELECT rn FROM shuffle_order WHERE shuffle_order.ROWID = demo.ROWID) - 1;
''');

    expect(result.errors, isEmpty);
  });

  group('resolves CTEs for', () {
    late SqlEngine engine;

    setUp(() {
      engine = SqlEngine()..registerTable(demoTable);
    });

    test('update statements', () {
      final result =
          engine.analyze('WITH x AS (SELECT * FROM demo) UPDATE demo '
              'SET content = x.content FROM x WHERE demo.id = x.id;');
      expect(result.errors, isEmpty);
    });

    test('update statements with column-name-list', () {
      final result = engine.analyze(
          'WITH x AS (SELECT * FROM demo) UPDATE demo '
          'SET (id, content) = (x.id, x.content) FROM x WHERE demo.id = x.id;');
      expect(result.errors, isEmpty);
    });

    test('insert statements', () {
      final result = engine.analyze(
          'WITH x AS (SELECT * FROM demo) INSERT INTO demo SELECT * FROM x;');
      expect(result.errors, isEmpty);
    });

    test('insert statements with upsert using column-name-list', () {
      final result = engine.analyze(
          'WITH x AS (SELECT * FROM demo) INSERT INTO demo SELECT * FROM x ON CONFLICT(content) DO UPDATE SET (id, content) = (0, \'hello\');');
      expect(result.errors, isEmpty);
    });

    test('delete statements', () {
      final result = engine.analyze(
          'WITH x AS (SELECT * FROM demo) DELETE FROM demo WHERE id IN (SELECT id FROM x);');
      expect(result.errors, isEmpty);
    });
  });
}
