import { v } from 'convex/values';

import { internalMutation, internalQuery, query } from './_generated/server';
import { getOwnedSpoon, getRequiredUserId } from './model';

const side = v.union(v.literal('upstream'), v.literal('fork'));

export const listForSpoon = query({
  args: {
    spoonId: v.id('spoons'),
    side: v.optional(side),
    limit: v.optional(v.number()),
  },
  handler: async (ctx, { spoonId, side, limit }) => {
    const ownerId = await getRequiredUserId(ctx);
    await getOwnedSpoon(ctx, spoonId, ownerId);
    if (side) {
      return await ctx.db
        .query('spoonCommits')
        .withIndex('by_spoon_side', (q) =>
          q.eq('spoonId', spoonId).eq('side', side),
        )
        .order('desc')
        .take(limit ?? 100);
    }
    const commits = await ctx.db
      .query('spoonCommits')
      .withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
      .order('desc')
      .collect();
    return commits
      .filter((commit) => commit.spoonId === spoonId)
      .slice(0, limit ?? 100);
  },
});

export const listInternal = internalQuery({
  args: {
    spoonId: v.id('spoons'),
    ownerId: v.id('users'),
    side,
    limit: v.optional(v.number()),
  },
  handler: async (ctx, { spoonId, ownerId, side, limit }) => {
    const rows = await ctx.db
      .query('spoonCommits')
      .withIndex('by_spoon_side', (q) =>
        q.eq('spoonId', spoonId).eq('side', side),
      )
      .order('desc')
      .take(limit ?? 100);
    return rows.filter((row) => row.ownerId === ownerId);
  },
});

export const replaceForSpoon = internalMutation({
  args: {
    spoonId: v.id('spoons'),
    ownerId: v.id('users'),
    side,
    commits: v.array(
      v.object({
        sha: v.string(),
        message: v.string(),
        authorName: v.optional(v.string()),
        authorEmail: v.optional(v.string()),
        authorLogin: v.optional(v.string()),
        committedAt: v.optional(v.number()),
        htmlUrl: v.optional(v.string()),
        filesChanged: v.optional(v.number()),
        additions: v.optional(v.number()),
        deletions: v.optional(v.number()),
      }),
    ),
  },
  handler: async (ctx, { spoonId, ownerId, side, commits }) => {
    const existing = await ctx.db
      .query('spoonCommits')
      .withIndex('by_spoon_side', (q) =>
        q.eq('spoonId', spoonId).eq('side', side),
      )
      .collect();
    await Promise.all(existing.map((commit) => ctx.db.delete(commit._id)));
    const now = Date.now();
    await Promise.all(
      commits.map((commit) =>
        ctx.db.insert('spoonCommits', {
          spoonId,
          ownerId,
          side,
          ...commit,
          createdAt: now,
          updatedAt: now,
        }),
      ),
    );
    return { success: true };
  },
});
