'use node';

import { v } from 'convex/values';

import type { Doc } from './_generated/dataModel';
import { internal } from './_generated/api';
import { action } from './_generated/server';
import { decryptSecret } from './secretCrypto';
import { assertWorkerToken } from './workerAuth';

type ClaimedJob = {
  job: Doc<'agentJobs'>;
  spoon: Doc<'spoons'> | null;
  aiSettings: Doc<'userAiSettings'> | null;
  aiProviderProfile: Doc<'aiProviderProfiles'> | null;
  agentSettings: Doc<'spoonAgentSettings'> | null;
  secrets: Doc<'spoonSecrets'>[];
};

type WorkerClaim = {
  job: Doc<'agentJobs'>;
  spoon: Doc<'spoons'>;
  openai: {
    apiKey?: string;
    model: string;
    reasoningEffort: Doc<'agentJobs'>['reasoningEffort'];
  };
  aiProviderProfile?: {
    id: string;
    name: string;
    provider: Doc<'aiProviderProfiles'>['provider'];
    authType: Doc<'aiProviderProfiles'>['authType'];
    secret?: string;
    baseUrl?: string;
    model: string;
    reasoningEffort: Doc<'aiProviderProfiles'>['reasoningEffort'];
  };
  agentSettings: Doc<'spoonAgentSettings'> | null;
  github: {
    installationId?: string;
  };
  secrets: { name: string; value: string }[];
};

export const claimNextForWorker = action({
  args: {
    workerId: v.string(),
    workerToken: v.string(),
  },
  handler: async (ctx, args): Promise<WorkerClaim | null> => {
    assertWorkerToken(args.workerToken);
    const claimed = (await ctx.runMutation(
      internal.agentJobs.claimNextInternal,
      {
        workerId: args.workerId,
      },
    )) as ClaimedJob | null;
    if (!claimed) return null;
    if (!claimed.spoon || !claimed.aiProviderProfile) return null;
    const profile = claimed.aiProviderProfile;
    let profileSecret: string | undefined;
    let secrets: { name: string; value: string }[];
    try {
      profileSecret = profile.encryptedSecret
        ? decryptSecret(profile.encryptedSecret)
        : undefined;
      secrets = claimed.secrets.map((secret: Doc<'spoonSecrets'>) => ({
        name: secret.name,
        value: decryptSecret(secret.encryptedValue),
      }));
    } catch {
      await ctx.runMutation(internal.agentJobs.failClaimInternal, {
        jobId: claimed.job._id,
        workerId: args.workerId,
        reason: 'Stored AI credentials or Spoon secrets could not be decrypted.',
      });
      return null;
    }
    return {
      job: claimed.job,
      spoon: claimed.spoon,
      openai: {
        model: profile.defaultModel,
        reasoningEffort: profile.reasoningEffort,
      },
      aiProviderProfile: {
        id: profile._id,
        name: profile.name,
        provider: profile.provider,
        authType: profile.authType,
        secret: profileSecret,
        baseUrl: profile.baseUrl,
        model: profile.defaultModel,
        reasoningEffort: profile.reasoningEffort,
      },
      agentSettings: claimed.agentSettings,
      github: {
        installationId:
          claimed.job.githubInstallationId ??
          claimed.spoon.githubInstallationId ??
          process.env.GITHUB_APP_INSTALLATION_ID,
      },
      secrets,
    };
  },
});
