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

import type { Doc } from './_generated/dataModel';
import { internalMutation, mutation, query } from './_generated/server';
import { getOwnedSpoon, getRequiredUserId } from './model';

const publicSecret = (secret: Doc<'spoonSecrets'>) => ({
  _id: secret._id,
  _creationTime: secret._creationTime,
  spoonId: secret.spoonId,
  ownerId: secret.ownerId,
  name: secret.name,
  valuePreview: secret.valuePreview,
  description: secret.description,
  createdAt: secret.createdAt,
  updatedAt: secret.updatedAt,
});

export const listForSpoon = query({
  args: { spoonId: v.id('spoons') },
  handler: async (ctx, { spoonId }) => {
    const ownerId = await getRequiredUserId(ctx);
    await getOwnedSpoon(ctx, spoonId, ownerId);
    const secrets = await ctx.db
      .query('spoonSecrets')
      .withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
      .order('asc')
      .collect();
    return secrets.map(publicSecret);
  },
});

export const remove = mutation({
  args: { secretId: v.id('spoonSecrets') },
  handler: async (ctx, { secretId }) => {
    const ownerId = await getRequiredUserId(ctx);
    const secret = await ctx.db.get(secretId);
    if (secret?.ownerId !== ownerId) {
      throw new ConvexError('Spoon secret not found.');
    }
    await ctx.db.delete(secretId);
    return { success: true };
  },
});

export const upsertEncryptedInternal = internalMutation({
  args: {
    spoonId: v.id('spoons'),
    ownerId: v.id('users'),
    name: v.string(),
    encryptedValue: v.string(),
    valuePreview: v.string(),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const existing = await ctx.db
      .query('spoonSecrets')
      .withIndex('by_name', (q) =>
        q.eq('spoonId', args.spoonId).eq('name', args.name),
      )
      .first();
    if (existing) {
      if (existing.ownerId !== args.ownerId) {
        throw new ConvexError('Spoon secret not found.');
      }
      await ctx.db.patch(existing._id, {
        encryptedValue: args.encryptedValue,
        valuePreview: args.valuePreview,
        description: args.description,
        updatedAt: now,
      });
      return existing._id;
    }
    return await ctx.db.insert('spoonSecrets', {
      ...args,
      createdAt: now,
      updatedAt: now,
    });
  },
});

export const patchEncryptedInternal = internalMutation({
  args: {
    secretId: v.id('spoonSecrets'),
    ownerId: v.id('users'),
    encryptedValue: v.optional(v.string()),
    valuePreview: v.optional(v.string()),
    description: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const secret = await ctx.db.get(args.secretId);
    if (secret?.ownerId !== args.ownerId) {
      throw new ConvexError('Spoon secret not found.');
    }
    await ctx.db.patch(args.secretId, {
      encryptedValue: args.encryptedValue ?? secret.encryptedValue,
      valuePreview: args.valuePreview ?? secret.valuePreview,
      description: args.description,
      updatedAt: Date.now(),
    });
    return { success: true };
  },
});
