import { mutation } from "../_generated/server";
import { v } from "convex/values";
import {
  nurtureSequenceTypeValidator,
  nurtureEmailStatusValidator,
} from "../schema/nurture";

// ── Sequence CRUD ───────────────────────────────────────────────────────

/**
 * Create a new nurture sequence
 */
export const createSequence = mutation({
  args: {
    name: v.string(),
    nameAr: v.optional(v.string()),
    description: v.optional(v.string()),
    sequenceType: nurtureSequenceTypeValidator,
    isActive: v.optional(v.boolean()),
    triggerEvent: v.string(),
    triggerConditions: v.optional(v.any()),
    createdBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("email_nurture_sequences", {
      name: args.name,
      nameAr: args.nameAr,
      description: args.description,
      sequenceType: args.sequenceType,
      isActive: args.isActive ?? true,
      triggerEvent: args.triggerEvent,
      triggerConditions: args.triggerConditions,
      createdBy: args.createdBy,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update an existing nurture sequence
 */
export const updateSequence = mutation({
  args: {
    id: v.id("email_nurture_sequences"),
    name: v.optional(v.string()),
    nameAr: v.optional(v.string()),
    description: v.optional(v.string()),
    sequenceType: v.optional(nurtureSequenceTypeValidator),
    isActive: v.optional(v.boolean()),
    triggerEvent: v.optional(v.string()),
    triggerConditions: v.optional(v.any()),
    updatedBy: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture sequence ${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;
  },
});

/**
 * Delete a nurture sequence and all its steps
 */
export const deleteSequence = mutation({
  args: { id: v.id("email_nurture_sequences") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture sequence ${args.id} not found`);
    }

    // Delete all steps belonging to this sequence
    const steps = await ctx.db
      .query("email_nurture_sequence_steps")
      .withIndex("by_sequenceId", (q) => q.eq("sequenceId", args.id))
      .collect();
    for (const step of steps) {
      await ctx.db.delete(step._id);
    }

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

// ── Step CRUD ───────────────────────────────────────────────────────────

/**
 * Create a new step in a nurture sequence
 */
export const createStep = mutation({
  args: {
    sequenceId: v.string(),
    stepNumber: v.number(),
    name: v.string(),
    subjectEn: v.string(),
    subjectAr: v.string(),
    delayHours: v.number(),
    templateKey: v.string(),
    isActive: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("email_nurture_sequence_steps", {
      sequenceId: args.sequenceId,
      stepNumber: args.stepNumber,
      name: args.name,
      subjectEn: args.subjectEn,
      subjectAr: args.subjectAr,
      delayHours: args.delayHours,
      templateKey: args.templateKey,
      isActive: args.isActive ?? true,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update an existing step
 */
export const updateStep = mutation({
  args: {
    id: v.id("email_nurture_sequence_steps"),
    name: v.optional(v.string()),
    subjectEn: v.optional(v.string()),
    subjectAr: v.optional(v.string()),
    delayHours: v.optional(v.number()),
    templateKey: v.optional(v.string()),
    isActive: v.optional(v.boolean()),
    stepNumber: v.optional(v.number()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture step ${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;
  },
});

/**
 * Delete a step from a sequence
 */
export const deleteStep = mutation({
  args: { id: v.id("email_nurture_sequence_steps") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture step ${args.id} not found`);
    }
    await ctx.db.delete(args.id);
    return args.id;
  },
});

/**
 * Reorder steps within a sequence (bulk update stepNumber)
 */
export const reorderSteps = mutation({
  args: {
    steps: v.array(
      v.object({
        id: v.id("email_nurture_sequence_steps"),
        stepNumber: v.number(),
      }),
    ),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    for (const step of args.steps) {
      const existing = await ctx.db.get(step.id);
      if (!existing) {
        throw new Error(`Nurture step ${step.id} not found`);
      }
      await ctx.db.patch(step.id, {
        stepNumber: step.stepNumber,
        updatedAt: now,
      });
    }
    return args.steps.length;
  },
});

// ── User Enrollment State ───────────────────────────────────────────────

/**
 * Enroll a user in a nurture sequence
 */
export const enrollUser = mutation({
  args: {
    userId: v.string(),
    sequenceId: v.string(),
    metadata: v.optional(v.any()),
  },
  handler: async (ctx, args) => {
    // Check for existing enrollment
    const existing = await ctx.db
      .query("user_nurture_sequence_states")
      .withIndex("by_userId_sequenceId", (q) =>
        q.eq("userId", args.userId).eq("sequenceId", args.sequenceId),
      )
      .first();

    if (existing && existing.status === "ACTIVE") {
      throw new Error(
        `User ${args.userId} is already enrolled in sequence ${args.sequenceId}`,
      );
    }

    const now = Date.now();
    const id = await ctx.db.insert("user_nurture_sequence_states", {
      userId: args.userId,
      sequenceId: args.sequenceId,
      currentStep: 0,
      status: "ACTIVE",
      startedAt: now,
      metadata: args.metadata,
    });
    return id;
  },
});

/**
 * Pause a user's nurture sequence
 */
export const pauseUser = mutation({
  args: { id: v.id("user_nurture_sequence_states") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture state ${args.id} not found`);
    }
    if (existing.status !== "ACTIVE") {
      throw new Error(`Cannot pause: state is ${existing.status}, not ACTIVE`);
    }
    await ctx.db.patch(args.id, {
      status: "PAUSED",
      pausedAt: Date.now(),
    });
    return args.id;
  },
});

/**
 * Resume a paused nurture sequence for a user
 */
export const resumeUser = mutation({
  args: { id: v.id("user_nurture_sequence_states") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture state ${args.id} not found`);
    }
    if (existing.status !== "PAUSED") {
      throw new Error(`Cannot resume: state is ${existing.status}, not PAUSED`);
    }
    await ctx.db.patch(args.id, {
      status: "ACTIVE",
      pausedAt: undefined,
    });
    return args.id;
  },
});

/**
 * Cancel a user's nurture sequence enrollment
 */
export const cancelUser = mutation({
  args: { id: v.id("user_nurture_sequence_states") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture state ${args.id} not found`);
    }
    if (existing.status === "COMPLETED" || existing.status === "CANCELLED") {
      throw new Error(`Cannot cancel: state is already ${existing.status}`);
    }
    await ctx.db.patch(args.id, {
      status: "CANCELLED",
      cancelledAt: Date.now(),
    });
    return args.id;
  },
});

/**
 * Mark a user's nurture sequence as completed
 */
export const completeUser = mutation({
  args: { id: v.id("user_nurture_sequence_states") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture state ${args.id} not found`);
    }
    if (existing.status !== "ACTIVE") {
      throw new Error(
        `Cannot complete: state is ${existing.status}, not ACTIVE`,
      );
    }
    await ctx.db.patch(args.id, {
      status: "COMPLETED",
      completedAt: Date.now(),
    });
    return args.id;
  },
});

// ── Email CRUD ──────────────────────────────────────────────────────────

/**
 * Create a nurture email record (e.g., when scheduling)
 */
export const createEmail = mutation({
  args: {
    stateId: v.string(),
    stepId: v.string(),
    userId: v.string(),
    email: v.string(),
    status: v.optional(nurtureEmailStatusValidator),
    scheduledFor: v.number(),
    metadata: v.optional(v.any()),
  },
  handler: async (ctx, args) => {
    const now = Date.now();
    const id = await ctx.db.insert("user_nurture_sequence_emails", {
      stateId: args.stateId,
      stepId: args.stepId,
      userId: args.userId,
      email: args.email,
      status: args.status ?? "PENDING",
      scheduledFor: args.scheduledFor,
      metadata: args.metadata,
      createdAt: now,
      updatedAt: now,
    });
    return id;
  },
});

/**
 * Update the status of a nurture email
 */
export const updateEmailStatus = mutation({
  args: {
    id: v.id("user_nurture_sequence_emails"),
    status: nurtureEmailStatusValidator,
    errorMessage: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture email ${args.id} not found`);
    }
    const patch: Record<string, unknown> = {
      status: args.status,
      updatedAt: Date.now(),
    };
    if (args.errorMessage !== undefined) {
      patch.errorMessage = args.errorMessage;
    }
    // Set timestamp fields based on status
    if (args.status === "FAILED") {
      patch.failedAt = Date.now();
    }
    await ctx.db.patch(args.id, patch);
    return args.id;
  },
});

/**
 * Mark a nurture email as sent
 */
export const markEmailSent = mutation({
  args: { id: v.id("user_nurture_sequence_emails") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture email ${args.id} not found`);
    }
    const now = Date.now();
    await ctx.db.patch(args.id, {
      status: "SENT",
      sentAt: now,
      updatedAt: now,
    });
    return args.id;
  },
});

/**
 * Mark a nurture email as opened
 */
export const markEmailOpened = mutation({
  args: { id: v.id("user_nurture_sequence_emails") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture email ${args.id} not found`);
    }
    const now = Date.now();
    await ctx.db.patch(args.id, {
      status: "OPENED",
      openedAt: now,
      updatedAt: now,
    });
    return args.id;
  },
});

/**
 * Mark a nurture email as clicked
 */
export const markEmailClicked = mutation({
  args: { id: v.id("user_nurture_sequence_emails") },
  handler: async (ctx, args) => {
    const existing = await ctx.db.get(args.id);
    if (!existing) {
      throw new Error(`Nurture email ${args.id} not found`);
    }
    const now = Date.now();
    await ctx.db.patch(args.id, {
      status: "CLICKED",
      clickedAt: now,
      updatedAt: now,
    });
    return args.id;
  },
});
