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

import type { Id } from '../../convex/_generated/dataModel.js';
import { api } from '../../convex/_generated/api.js';
import schema from '../../convex/schema';

const modules = import.meta.glob('../../convex/**/*.*s');

const createUser = async (t: ReturnType<typeof convexTest>, email: string) =>
  (await t.mutation(async (ctx) => {
    return await ctx.db.insert('users', { email, name: email });
  })) as Id<'users'>;

const authed = (t: ReturnType<typeof convexTest>, userId: string) =>
  t.withIdentity({
    subject: `${userId}|session`,
    issuer: 'https://convex.test',
  });

const githubSpoonInput = {
  name: 'Editor Spoon',
  provider: 'github' as const,
  upstreamOwner: 'upstream',
  upstreamRepo: 'editor',
  upstreamDefaultBranch: 'main',
  upstreamUrl: 'https://github.com/upstream/editor',
  forkOwner: 'team',
  forkRepo: 'editor-spoon',
  forkUrl: 'https://github.com/team/editor-spoon',
  visibility: 'private' as const,
  maintenanceMode: 'watch' as const,
  syncCadence: 'daily' as const,
  productionRefStrategy: 'default_branch' as const,
};

const setup = async (
  t: ReturnType<typeof convexTest>,
  profile: {
    provider: 'anthropic' | 'opencode_openai_login';
    authType: 'api_key' | 'opencode_auth_json';
  },
) => {
  const ownerId = await createUser(t, 'owner@example.com');
  const spoonId = await authed(t, ownerId).mutation(
    api.spoons.createManual,
    githubSpoonInput,
  );
  const threadId = await t.mutation(async (ctx) => {
    const now = Date.now();
    await ctx.db.insert('aiProviderProfiles', {
      ownerId,
      name: 'Primary provider',
      provider: profile.provider,
      authType: profile.authType,
      encryptedSecret: 'encrypted-secret',
      secretPreview: 'sk-...abcd',
      defaultModel: 'claude-sonnet-4',
      modelOptions: ['claude-sonnet-4'],
      reasoningEffort: 'medium',
      enabled: true,
      createdAt: now,
      updatedAt: now,
    });
    return await ctx.db.insert('threads', {
      ownerId,
      spoonId,
      title: 'Runtime thread',
      summary: 'do the work',
      source: 'user_request',
      status: 'open',
      priority: 'normal',
      createdAt: now,
      updatedAt: now,
    });
  });
  return { ownerId, spoonId, threadId };
};

describe('queue-time runtime validation', () => {
  test('rejects a runtime the provider profile cannot drive', async () => {
    const t = convexTest(schema, modules);
    const { ownerId, threadId } = await setup(t, {
      provider: 'anthropic',
      authType: 'api_key',
    });

    await expect(
      authed(t, ownerId).mutation(api.agentJobs.createForThread, {
        threadId,
        jobType: 'user_change',
        runtime: 'codex',
      }),
    ).rejects.toThrow(/cannot run the "codex" runtime/);
  });

  test('resolves a supported requested runtime onto the job', async () => {
    const t = convexTest(schema, modules);
    const { ownerId, threadId } = await setup(t, {
      provider: 'anthropic',
      authType: 'api_key',
    });

    const jobId = await authed(t, ownerId).mutation(
      api.agentJobs.createForThread,
      {
        threadId,
        jobType: 'user_change',
        runtime: 'claude',
      },
    );
    const job = await t.run(async (ctx) => await ctx.db.get(jobId));
    expect(job?.runtime).toBe('claude');
  });

  test('defaults a ChatGPT-login profile job to the codex runtime', async () => {
    const t = convexTest(schema, modules);
    const { ownerId, threadId } = await setup(t, {
      provider: 'opencode_openai_login',
      authType: 'opencode_auth_json',
    });

    const jobId = await authed(t, ownerId).mutation(
      api.agentJobs.createForThread,
      {
        threadId,
        jobType: 'user_change',
      },
    );
    const job = await t.run(async (ctx) => await ctx.db.get(jobId));
    expect(job?.runtime).toBe('codex');
  });
});
