'use node';

import { ConvexError, v } from 'convex/values';

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

type ClaimedJob = {
  job: Doc<'agentJobs'>;
  spoon: Doc<'spoons'> | null;
  aiSettings: Doc<'userAiSettings'> | 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'];
  };
  agentSettings: Doc<'spoonAgentSettings'> | null;
  github: {
    installationId?: string;
  };
  secrets: { name: string; value: string }[];
};

const requireWorkerToken = (workerToken: string) => {
  const expected = process.env.SPOON_WORKER_TOKEN?.trim();
  if (!expected) throw new ConvexError('SPOON_WORKER_TOKEN is not configured.');
  if (workerToken !== expected) throw new ConvexError('Invalid worker token.');
};

export const claimNextForWorker = action({
  args: {
    workerId: v.string(),
    workerToken: v.string(),
  },
  handler: async (ctx, args): Promise<WorkerClaim | null> => {
    requireWorkerToken(args.workerToken);
    const claimed = (await ctx.runMutation(
      internal.agentJobs.claimNextInternal,
      {
        workerId: args.workerId,
      },
    )) as ClaimedJob | null;
    if (!claimed) return null;
    if (!claimed.spoon) {
      throw new ConvexError('Claimed job points at a missing Spoon.');
    }
    if (!claimed.aiSettings?.encryptedApiKey) {
      throw new ConvexError(
        'OpenAI is not configured for this user. Add an OpenAI API key in settings.',
      );
    }
    return {
      job: claimed.job,
      spoon: claimed.spoon,
      openai: {
        apiKey: decryptSecret(claimed.aiSettings.encryptedApiKey),
        model: claimed.job.model,
        reasoningEffort: claimed.job.reasoningEffort,
      },
      agentSettings: claimed.agentSettings,
      github: {
        installationId:
          claimed.job.githubInstallationId ??
          claimed.spoon.githubInstallationId ??
          process.env.GITHUB_APP_INSTALLATION_ID,
      },
      secrets: claimed.secrets.map((secret: Doc<'spoonSecrets'>) => ({
        name: secret.name,
        value: decryptSecret(secret.encryptedValue),
      })),
    };
  },
});
