'use node';

import { getAuthUserId } from '@convex-dev/auth/server';
import { ConvexError, v } from 'convex/values';

import type { Id } from './_generated/dataModel';
import type { ActionCtx } from './_generated/server';
import { internal } from './_generated/api';
import { action } from './_generated/server';
import { encryptSecret } from './secretCrypto';

const secretNamePattern = /^[A-Z_][A-Z0-9_]*$/;

const getRequiredUserId = async (ctx: ActionCtx): Promise<Id<'users'>> => {
  const userId = await getAuthUserId(ctx);
  if (!userId) throw new ConvexError('Not authenticated.');
  return userId;
};

const previewSecret = (value: string) => {
  const trimmed = value.trim();
  if (!trimmed) return 'empty';
  if (trimmed.length <= 8) return 'configured';
  return `${trimmed.slice(0, 3)}...${trimmed.slice(-3)}`;
};

const normalizeName = (name: string) => {
  const normalized = name.trim().toUpperCase();
  if (!secretNamePattern.test(normalized)) {
    throw new ConvexError(
      'Secret names must look like environment variables, for example AUTH_SECRET.',
    );
  }
  return normalized;
};

const optionalText = (value?: string) => {
  const trimmed = value?.trim();
  if (!trimmed) return undefined;
  return trimmed;
};

export const create = action({
  args: {
    spoonId: v.id('spoons'),
    name: v.string(),
    value: v.string(),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args): Promise<Id<'spoonSecrets'>> => {
    const ownerId = await getRequiredUserId(ctx);
    await ctx.runQuery(internal.spoons.getOwnedForAction, {
      spoonId: args.spoonId,
      ownerId,
    });
    const value = args.value.trim();
    if (!value) throw new ConvexError('Secret value is required.');
    return await ctx.runMutation(
      internal.spoonSecrets.upsertEncryptedInternal,
      {
        spoonId: args.spoonId,
        ownerId,
        name: normalizeName(args.name),
        encryptedValue: encryptSecret(value),
        valuePreview: previewSecret(value),
        description: optionalText(args.description),
      },
    );
  },
});

export const update = action({
  args: {
    secretId: v.id('spoonSecrets'),
    value: v.optional(v.string()),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args): Promise<{ success: true }> => {
    const ownerId = await getRequiredUserId(ctx);
    const patch = args.value?.trim()
      ? {
          encryptedValue: encryptSecret(args.value.trim()),
          valuePreview: previewSecret(args.value.trim()),
        }
      : {};
    await ctx.runMutation(internal.spoonSecrets.patchEncryptedInternal, {
      secretId: args.secretId,
      ownerId,
      description: optionalText(args.description),
      ...patch,
    });
    return { success: true };
  },
});
