import { convexTest } from 'convex-test';
import { makeFunctionReference } from 'convex/server';
import { describe, expect, test } from 'vitest';

import { internal } from '../../convex/_generated/api.js';
import schema from '../../convex/schema';
import {
  shouldAutoSync,
  shouldCreateReviewThread,
} from '../../convex/syncGating';

const modules = import.meta.glob('../../convex/**/*.*s');
const backfillAutoSyncDefaults = makeFunctionReference<
  'mutation',
  Record<string, never>,
  { updated: number }
>('migrations:backfillAutoSyncDefaults');

describe('syncGating', () => {
  test('auto-sync requires the flag and a clean behind compare', () => {
    expect(
      shouldAutoSync(
        { autoSyncEnabled: false, requireCleanCompareForSync: true },
        { status: 'behind', forkAheadBy: 0 },
      ),
    ).toBe(false);
    expect(
      shouldAutoSync(
        { autoSyncEnabled: true, requireCleanCompareForSync: true },
        { status: 'behind', forkAheadBy: 0 },
      ),
    ).toBe(true);
    expect(
      shouldAutoSync(
        { autoSyncEnabled: true, requireCleanCompareForSync: true },
        { status: 'behind', forkAheadBy: 2 },
      ),
    ).toBe(false);
    expect(
      shouldAutoSync(
        { autoSyncEnabled: true, requireCleanCompareForSync: true },
        { status: 'diverged', forkAheadBy: 0 },
      ),
    ).toBe(false);
  });

  test('review thread requires autoReviewEnabled and diverged', () => {
    expect(
      shouldCreateReviewThread(
        { autoReviewEnabled: false },
        { status: 'diverged' },
      ),
    ).toBe(false);
    expect(
      shouldCreateReviewThread(
        { autoReviewEnabled: true },
        { status: 'diverged' },
      ),
    ).toBe(true);
    expect(
      shouldCreateReviewThread(
        { autoReviewEnabled: true },
        { status: 'behind' },
      ),
    ).toBe(false);
  });

  test('backfill enables auto-sync only for disabled GitHub spoons', async () => {
    const t = convexTest(schema, modules);
    const seeded = await t.mutation(async (ctx) => {
      const now = Date.now();
      const ownerId = await ctx.db.insert('users', {
        email: 'owner@example.com',
        name: 'Owner',
      });
      const createSpoon = async (
        name: string,
        provider: 'github' | 'gitea',
        autoSyncEnabled: boolean,
      ) => {
        const spoonId = await ctx.db.insert('spoons', {
          ownerId,
          name,
          provider,
          upstreamOwner: 'upstream',
          upstreamRepo: name,
          upstreamDefaultBranch: 'main',
          upstreamUrl: `https://example.com/upstream/${name}`,
          forkOwner: 'owner',
          forkRepo: name,
          forkUrl: `https://example.com/owner/${name}`,
          visibility: 'private',
          maintenanceMode: 'watch',
          syncCadence: 'daily',
          productionRefStrategy: 'default_branch',
          status: 'active',
          createdAt: now,
          updatedAt: now,
        });
        const settingsId = await ctx.db.insert('spoonSettings', {
          spoonId,
          ownerId,
          autoRefreshEnabled: true,
          autoReviewEnabled: true,
          autoSyncEnabled,
          requireAiLowRiskForSync: true,
          requireCleanCompareForSync: true,
          ignoredFilePatterns: [],
          importantFilePatterns: [],
          createdAt: now,
          updatedAt: now,
        });
        return settingsId;
      };

      return {
        disabledGithub: await createSpoon('disabled-github', 'github', false),
        enabledGithub: await createSpoon('enabled-github', 'github', true),
        disabledGitea: await createSpoon('disabled-gitea', 'gitea', false),
      };
    });

    const result = await t.mutation(backfillAutoSyncDefaults, {});
    const settings = await t.run(async (ctx) => ({
      disabledGithub: await ctx.db.get(seeded.disabledGithub),
      enabledGithub: await ctx.db.get(seeded.enabledGithub),
      disabledGitea: await ctx.db.get(seeded.disabledGitea),
    }));

    expect(result).toEqual({ updated: 1 });
    expect(settings.disabledGithub?.autoSyncEnabled).toBe(true);
    expect(settings.enabledGithub?.autoSyncEnabled).toBe(true);
    expect(settings.disabledGitea?.autoSyncEnabled).toBe(false);
  });

  test('new GitHub spoons default auto-sync on', async () => {
    const t = convexTest(schema, modules);
    const ownerId = await t.mutation(async (ctx) => {
      return await ctx.db.insert('users', {
        email: 'new-owner@example.com',
        name: 'New owner',
      });
    });

    const spoonId = await t.mutation(internal.github.createForkSpoonRecord, {
      ownerId,
      name: 'github-spoon',
      upstreamOwner: 'upstream',
      upstreamRepo: 'repo',
      upstreamDefaultBranch: 'main',
      upstreamUrl: 'https://github.com/upstream/repo',
      forkOwner: 'new-owner',
      forkRepo: 'repo',
      forkDefaultBranch: 'main',
      forkUrl: 'https://github.com/new-owner/repo',
      visibility: 'private',
    });
    const settings = await t.run(async (ctx) => {
      return await ctx.db
        .query('spoonSettings')
        .withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
        .unique();
    });

    expect(settings?.autoSyncEnabled).toBe(true);
  });
});
