import { mutation } from "../_generated/server";
import { v } from "convex/values";

/**
 * Create a new workflow definition
 * WU-18: Workflows domain
 */
export const createWorkflow = mutation({
  args: {
    userId: v.string(),
    workflowType: v.string(),
    n8nWorkflowId: v.optional(v.string()),
    config: v.optional(v.any()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();

    // Check for duplicate
    const existing = await ctx.db
      .query("user_workflows")
      .withIndex("by_userId_workflowType", (q) =>
        q.eq("userId", args.userId).eq("workflowType", args.workflowType),
      )
      .unique();

    if (existing) {
      throw new Error(`Workflow of type ${args.workflowType} already exists`);
    }

    const id = await ctx.db.insert("user_workflows", {
      userId: args.userId,
      workflowType: args.workflowType,
      n8nWorkflowId: args.n8nWorkflowId,
      status: "inactive",
      config: args.config ?? {},
      errorCount: 0,
      healthStatus: "healthy",
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update workflow configuration
 * WU-18: Workflows domain
 */
export const updateWorkflow = mutation({
  args: {
    id: v.id("user_workflows"),
    workflowType: v.optional(v.string()),
    n8nWorkflowId: v.optional(v.string()),
    config: v.optional(v.any()),
    status: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Workflow ${args.id} not found`);
    }

    const { id, ...fields } = args;
    const patch: Record<string, unknown> = { updatedAt: Date.now() };
    for (const [key, value] of Object.entries(fields)) {
      if (value !== undefined) patch[key] = value;
    }

    await ctx.db.patch(id, patch);
    return id;
  },
});

/**
 * Activate a workflow
 * WU-18: Workflows domain
 */
export const activateWorkflow = mutation({
  args: {
    id: v.id("user_workflows"),
    userId: v.string(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error("Workflow not found");
    }
    if (existing.userId !== args.userId) {
      throw new Error("Workflow not found or unauthorized");
    }

    await ctx.db.patch(args.id, {
      status: "active",
      updatedAt: Date.now(),
    });
    return args.id;
  },
});

/**
 * Deactivate a workflow
 * WU-18: Workflows domain
 */
export const deactivateWorkflow = mutation({
  args: {
    id: v.id("user_workflows"),
    userId: v.string(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error("Workflow not found");
    }
    if (existing.userId !== args.userId) {
      throw new Error("Workflow not found or unauthorized");
    }

    await ctx.db.patch(args.id, {
      status: "inactive",
      updatedAt: Date.now(),
    });
    return args.id;
  },
});

/**
 * Delete a workflow
 * WU-18: Workflows domain
 */
export const deleteWorkflow = mutation({
  args: {
    id: v.id("user_workflows"),
    userId: v.string(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error("Workflow not found");
    }
    if (existing.userId !== args.userId) {
      throw new Error("Workflow not found or unauthorized");
    }

    await ctx.db.delete(args.id);
    return { deleted: true };
  },
});

/**
 * Create a workflow execution record (log a run)
 * WU-18: Workflows domain
 */
export const createWorkflowExecution = mutation({
  args: {
    id: v.id("user_workflows"),
    status: v.string(),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Workflow ${args.id} not found`);
    }

    const now = Date.now();
    const patch: Record<string, unknown> = {
      lastRunAt: now,
      lastRunStatus: args.status,
      updatedAt: now,
    };

    if (args.status === "error") {
      patch.errorCount = existing.errorCount + 1;
      patch.lastErrorAt = now;
      if (existing.errorCount + 1 >= 5) {
        patch.healthStatus = "critical";
      } else if (existing.errorCount + 1 >= 3) {
        patch.healthStatus = "degraded";
      }
    } else {
      // On success, reset error count and set healthy
      patch.errorCount = 0;
      patch.healthStatus = "healthy";
    }

    await ctx.db.patch(args.id, patch);
    return args.id;
  },
});

/**
 * Update a workflow execution status
 * WU-18: Workflows domain
 */
export const updateExecutionStatus = mutation({
  args: {
    id: v.id("user_workflows"),
    status: v.string(),
    errorMessage: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Workflow ${args.id} not found`);
    }

    const now = Date.now();
    const patch: Record<string, unknown> = {
      lastRunStatus: args.status,
      updatedAt: now,
    };

    if (args.status === "error") {
      patch.errorCount = existing.errorCount + 1;
      patch.lastErrorAt = now;
    }

    await ctx.db.patch(args.id, patch);
    return args.id;
  },
});

/**
 * Process pending jobs from the sync queue
 * WU-18: Workflows domain (job-worker cron)
 */
export const processJobQueue = mutation({
  args: {
    jobId: v.id("sync_queue"),
    status: v.string(),
    error: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const job = await ctx.db.get(args.jobId);
    if (!job) {
      throw new Error(`Job ${args.jobId} not found`);
    }

    if (args.status === "completed") {
      await ctx.db.delete(args.jobId);
      return { processed: true };
    }

    // Failed - update attempts
    const newAttempts = job.attempts + 1;
    if (newAttempts >= job.maxAttempts) {
      // Move to DLQ
      await ctx.db.insert("sync_queue_dlq", {
        originalId: args.jobId as unknown as string,
        table: job.table,
        operation: job.operation,
        data: job.data,
        conflictColumn: job.conflictColumn,
        attempts: newAttempts,
        lastError: args.error,
        originalCreated: job.createdAt,
        movedToDlqAt: Date.now(),
      });
      await ctx.db.delete(args.jobId);
      return { movedToDlq: true };
    }

    // Retry with exponential backoff
    const delay = Math.min(1000 * Math.pow(2, newAttempts), 300000);
    await ctx.db.patch(args.jobId, {
      attempts: newAttempts,
      lastError: args.error,
      nextRetryAt: Date.now() + delay,
    });

    return { retrying: true, attempt: newAttempts };
  },
});
