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

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

export const getInstallUrl = query({
  args: {},
  handler: () => {
    const slug = process.env.GITHUB_APP_SLUG;
    if (!slug) return null;
    return `https://github.com/apps/${slug}/installations/new`;
  },
});

export const getConnection = query({
  args: {},
  handler: async (ctx) => {
    const userId = await getRequiredUserId(ctx);
    return await ctx.db
      .query('gitConnections')
      .withIndex('by_user_provider', (q) =>
        q.eq('userId', userId).eq('provider', 'github'),
      )
      .first();
  },
});

export const connectInstallation = mutation({
  args: { installationId: v.string() },
  handler: async (ctx, { installationId }) => {
    const userId = await getRequiredUserId(ctx);
    const trimmedInstallationId = installationId.trim();
    if (!trimmedInstallationId) {
      throw new ConvexError('GitHub installation ID is required.');
    }

    const now = Date.now();
    const existing = await ctx.db
      .query('gitConnections')
      .withIndex('by_user_provider', (q) =>
        q.eq('userId', userId).eq('provider', 'github'),
      )
      .first();

    const patch = {
      provider: 'github' as const,
      displayName: `GitHub installation ${trimmedInstallationId}`,
      installationId: trimmedInstallationId,
      scopes: [
        'metadata:read',
        'administration:write',
        'contents:write',
        'pull_requests:write',
      ],
      status: 'active' as const,
      updatedAt: now,
    };

    if (existing) {
      await ctx.db.patch(existing._id, patch);
      return existing._id;
    }

    return await ctx.db.insert('gitConnections', {
      userId,
      ...patch,
      connectedAt: now,
    });
  },
});

export const getConnectionForUser = internalQuery({
  args: { userId: v.id('users') },
  handler: async (ctx, { userId }) => {
    return await ctx.db
      .query('gitConnections')
      .withIndex('by_user_provider', (q) =>
        q.eq('userId', userId).eq('provider', 'github'),
      )
      .first();
  },
});

export const upsertConnectionForUser = internalMutation({
  args: {
    userId: v.id('users'),
    providerAccountId: v.optional(v.string()),
    displayName: v.string(),
    username: v.optional(v.string()),
    avatarUrl: v.optional(v.string()),
    installationId: v.string(),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const existing = await ctx.db
      .query('gitConnections')
      .withIndex('by_user_provider', (q) =>
        q.eq('userId', args.userId).eq('provider', 'github'),
      )
      .first();

    const patch = {
      providerAccountId: args.providerAccountId,
      displayName: args.displayName,
      username: args.username,
      avatarUrl: args.avatarUrl,
      installationId: args.installationId,
      scopes: [
        'metadata:read',
        'administration:write',
        'contents:write',
        'pull_requests:write',
        'checks:write',
        'statuses:write',
        'issues:write',
      ],
      status: 'active' as const,
      updatedAt: now,
    };

    if (existing) {
      await ctx.db.patch(existing._id, patch);
      return existing._id;
    }

    return await ctx.db.insert('gitConnections', {
      userId: args.userId,
      provider: 'github',
      ...patch,
      connectedAt: now,
    });
  },
});

export const createForkSpoonRecord = internalMutation({
  args: {
    ownerId: v.id('users'),
    name: v.string(),
    description: v.optional(v.string()),
    upstreamOwner: v.string(),
    upstreamRepo: v.string(),
    upstreamDefaultBranch: v.string(),
    upstreamUrl: v.string(),
    forkOwner: v.string(),
    forkRepo: v.string(),
    forkDefaultBranch: v.string(),
    forkUrl: v.string(),
    visibility: v.union(
      v.literal('public'),
      v.literal('private'),
      v.literal('internal'),
      v.literal('unknown'),
    ),
    connectionId: v.optional(v.id('gitConnections')),
  },
  handler: async (ctx, args): Promise<Id<'spoons'>> => {
    const now = Date.now();
    const spoonId = await ctx.db.insert('spoons', {
      ownerId: args.ownerId,
      name: args.name,
      description: args.description,
      provider: 'github',
      upstreamOwner: args.upstreamOwner,
      upstreamRepo: args.upstreamRepo,
      upstreamDefaultBranch: args.upstreamDefaultBranch,
      upstreamUrl: args.upstreamUrl,
      forkOwner: args.forkOwner,
      forkRepo: args.forkRepo,
      forkDefaultBranch: args.forkDefaultBranch,
      forkUrl: args.forkUrl,
      visibility: args.visibility,
      maintenanceMode: 'watch',
      syncCadence: 'daily',
      productionRefStrategy: 'default_branch',
      status: 'active',
      syncStatus: 'unknown',
      connectionId: args.connectionId,
      createdAt: now,
      updatedAt: now,
    });

    await ctx.db.insert('spoonSettings', {
      spoonId,
      ownerId: args.ownerId,
      autoRefreshEnabled: true,
      autoReviewEnabled: true,
      autoSyncEnabled: false,
      requireAiLowRiskForSync: true,
      requireCleanCompareForSync: true,
      ignoredFilePatterns: [],
      importantFilePatterns: [],
      createdAt: now,
      updatedAt: now,
    });

    await ctx.db.insert('syncRuns', {
      spoonId,
      ownerId: args.ownerId,
      kind: 'manual_check',
      status: 'clean',
      summary: `Created GitHub fork ${args.forkOwner}/${args.forkRepo} from ${args.upstreamOwner}/${args.upstreamRepo}.`,
      createdAt: now,
      updatedAt: now,
    });

    return spoonId;
  },
});
