'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;
  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 }[];
};

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.aiProviderProfile) {
      throw new ConvexError(
        'AI is not configured for this user. Add an AI provider in settings.',
      );
    }
    if (
      claimed.aiProviderProfile.authType !== 'none' &&
      !claimed.aiProviderProfile.encryptedSecret
    ) {
      throw new ConvexError('Selected AI provider is missing credentials.');
    }
    const profile = claimed.aiProviderProfile;
    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: profile.encryptedSecret
          ? decryptSecret(profile.encryptedSecret)
          : undefined,
        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: claimed.secrets.map((secret: Doc<'spoonSecrets'>) => ({
        name: secret.name,
        value: decryptSecret(secret.encryptedValue),
      })),
    };
  },
});
