import { v } from 'convex/values';

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

const syncKind = v.union(
  v.literal('scheduled_check'),
  v.literal('manual_check'),
  v.literal('upstream_update'),
  v.literal('merge_attempt'),
  v.literal('ai_review'),
);

const syncStatus = v.union(
  v.literal('queued'),
  v.literal('running'),
  v.literal('clean'),
  v.literal('conflict'),
  v.literal('needs_review'),
  v.literal('failed'),
  v.literal('merged'),
);

const syncDecision = v.union(
  v.literal('auto_synced'),
  v.literal('thread_created'),
  v.literal('ignored'),
  v.literal('failed'),
);

export const listRecent = query({
  args: { limit: v.optional(v.number()) },
  handler: async (ctx, { limit }) => {
    const ownerId = await getRequiredUserId(ctx);
    const runs = await ctx.db
      .query('syncRuns')
      .withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
      .order('desc')
      .take(limit ?? 25);
    return runs;
  },
});

export const listForSpoon = query({
  args: { spoonId: v.id('spoons'), limit: v.optional(v.number()) },
  handler: async (ctx, { spoonId, limit }) => {
    const ownerId = await getRequiredUserId(ctx);
    await getOwnedSpoon(ctx, spoonId, ownerId);
    return await ctx.db
      .query('syncRuns')
      .withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
      .order('desc')
      .take(limit ?? 25);
  },
});

export const createInternal = internalMutation({
  args: {
    spoonId: v.id('spoons'),
    ownerId: v.id('users'),
    threadId: v.optional(v.id('threads')),
    kind: syncKind,
    status: syncStatus,
    upstreamFrom: v.optional(v.string()),
    upstreamTo: v.optional(v.string()),
    summary: v.optional(v.string()),
    aiAssessment: v.optional(v.string()),
    mergeRequestUrl: v.optional(v.string()),
    error: v.optional(v.string()),
    decision: v.optional(syncDecision),
  },
  handler: async (ctx, args): Promise<Id<'syncRuns'>> => {
    const now = Date.now();
    return await ctx.db.insert('syncRuns', {
      ...args,
      createdAt: now,
      updatedAt: now,
    });
  },
});

export const patchInternal = internalMutation({
  args: {
    syncRunId: v.id('syncRuns'),
    threadId: v.optional(v.id('threads')),
    status: v.optional(syncStatus),
    upstreamFrom: v.optional(v.string()),
    upstreamTo: v.optional(v.string()),
    summary: v.optional(v.string()),
    aiAssessment: v.optional(v.string()),
    mergeRequestUrl: v.optional(v.string()),
    error: v.optional(v.string()),
    decision: v.optional(syncDecision),
  },
  handler: async (ctx, args) => {
    const patch: Partial<Doc<'syncRuns'>> = { updatedAt: Date.now() };
    if (args.threadId !== undefined) patch.threadId = args.threadId;
    if (args.status !== undefined) patch.status = args.status;
    if (args.upstreamFrom !== undefined) patch.upstreamFrom = args.upstreamFrom;
    if (args.upstreamTo !== undefined) patch.upstreamTo = args.upstreamTo;
    if (args.summary !== undefined) patch.summary = args.summary;
    if (args.aiAssessment !== undefined) {
      patch.aiAssessment = args.aiAssessment;
    }
    if (args.mergeRequestUrl !== undefined) {
      patch.mergeRequestUrl = args.mergeRequestUrl;
    }
    if (args.error !== undefined) patch.error = args.error;
    if (args.decision !== undefined) patch.decision = args.decision;
    await ctx.db.patch(args.syncRunId, patch);
    return { success: true };
  },
});
