import { runAutomationAIText } from "@/lib/ai/automation";
import { logProjectActivity } from "@/lib/project-activity";
import { logTeamAudit } from "@/lib/team-audit";
import { deliverGoogleSheetsIntegrationEvent } from "@/lib/integrations/google-sheets";
import { createGmailDraft } from "@/lib/integrations/google-workspace";
import { deliverNotionIntegrationEvent } from "@/lib/integrations/notion";
import { deliverSlackIntegrationEvent } from "@/lib/integrations/slack";
import { deliverTelegramIntegrationEvent } from "@/lib/integrations/telegram";
import { createAdminClient } from "@/lib/supabase/admin";
import { isTaskDeadlineOverdue } from "@/lib/utils";
import type { WebhookEventEnvelope } from "@/lib/developers/webhooks";
import type {
  AutomationActionConfig,
  AutomationActionType,
  AutomationRule,
  AutomationStep,
  IntegrationConnection,
  IntegrationProvider,
} from "@/types";

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

function getPrimaryEntity(data: Record<string, unknown>) {
  for (const key of ["task", "project", "note", "event"] as const) {
    const candidate = data[key];
    if (isRecord(candidate)) {
      return { kind: key, value: candidate };
    }
  }

  return null;
}

function getPrimaryTitle(data: Record<string, unknown>) {
  const primary = getPrimaryEntity(data);
  if (!primary) return null;

  const name = primary.value.name;
  if (typeof name === "string" && name.trim()) return name.trim();

  const title = primary.value.title;
  if (typeof title === "string" && title.trim()) return title.trim();

  return null;
}

function getNestedValue(source: Record<string, unknown>, path: string) {
  return path.split(".").reduce<unknown>((current, key) => {
    if (!isRecord(current)) return undefined;
    return current[key];
  }, source);
}

function renderTemplate(template: string, envelope: WebhookEventEnvelope) {
  const scope: Record<string, unknown> = {
    event: {
      id: envelope.id,
      type: envelope.type,
      created_at: envelope.created_at,
      actor_id: envelope.actor_id,
    },
    workspace: envelope.workspace,
    ...envelope.data,
  };

  return template.replace(/{{\s*([\w.]+)\s*}}/g, (_, path: string) => {
    const value = getNestedValue(scope, path);
    if (value == null) return "";
    if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
      return String(value);
    }
    return "";
  });
}

function createTextDocument(text: string) {
  const paragraphs = text
    .split(/\n+/)
    .map((line) => line.trim())
    .filter(Boolean);

  return {
    type: "doc",
    content:
      paragraphs.length > 0
        ? paragraphs.map((line) => ({
            type: "paragraph",
            content: [{ type: "text", text: line }],
          }))
        : [{ type: "paragraph", content: [] }],
  };
}

function getRuleSteps(rule: AutomationRule): AutomationStep[] {
  if (Array.isArray(rule.steps) && rule.steps.length > 0) {
    return rule.steps;
  }

  return [
    {
      id: "step_1",
      action_type: rule.action_type,
      action_config: rule.action_config,
    },
  ];
}

function getSourceProjectId(envelope: WebhookEventEnvelope) {
  const task = isRecord(envelope.data.task) ? envelope.data.task : null;
  const project = isRecord(envelope.data.project) ? envelope.data.project : null;
  const note = isRecord(envelope.data.note) ? envelope.data.note : null;

  return (
    (typeof task?.project_id === "string" && task.project_id) ||
    (typeof note?.project_id === "string" && note.project_id) ||
    (typeof project?.id === "string" && project.id) ||
    null
  );
}

function getSourceGoalId(envelope: WebhookEventEnvelope) {
  const task = isRecord(envelope.data.task) ? envelope.data.task : null;
  const project = isRecord(envelope.data.project) ? envelope.data.project : null;

  return (
    (typeof task?.goal_id === "string" && task.goal_id) ||
    (typeof project?.goal_id === "string" && project.goal_id) ||
    null
  );
}

function matchesConditions(rule: AutomationRule, envelope: WebhookEventEnvelope) {
  const conditions = rule.conditions || {};
  const data = envelope.data || {};
  const primaryTitle = getPrimaryTitle(data) || "";
  const task = isRecord(data.task) ? data.task : null;
  const previousTask = isRecord(data.previous_task) ? data.previous_task : null;
  const project = isRecord(data.project) ? data.project : null;
  const note = isRecord(data.note) ? data.note : null;
  const taskTags = Array.isArray(task?.tags)
    ? task.tags.filter((value): value is Record<string, unknown> => isRecord(value))
    : [];
  const taskDeadline =
    typeof task?.deadline === "string" && task.deadline ? new Date(task.deadline) : null;

  if (conditions.title_contains?.trim()) {
    const needle = conditions.title_contains.trim().toLowerCase();
    if (!primaryTitle.toLowerCase().includes(needle)) {
      return false;
    }
  }

  if (conditions.task_status_is) {
    if (!task || task.status !== conditions.task_status_is) {
      return false;
    }
  }

  if (conditions.task_status_changed_to) {
    if (!task || task.status !== conditions.task_status_changed_to) {
      return false;
    }
    if (!previousTask || previousTask.status === task.status) {
      return false;
    }
  }

  if (conditions.task_priority_is) {
    if (!task || task.priority !== conditions.task_priority_is) {
      return false;
    }
  }

  if (conditions.project_health_is) {
    if (!project || project.health !== conditions.project_health_is) {
      return false;
    }
  }

  if (conditions.task_deadline_state) {
    if (!task) {
      return false;
    }

    const hasDeadline = Boolean(taskDeadline && !Number.isNaN(taskDeadline.getTime()));
    const isOverdue =
      hasDeadline &&
      isTaskDeadlineOverdue(taskDeadline!) &&
      task.status !== "done" &&
      task.status !== "cancelled";

    if (conditions.task_deadline_state === "has_deadline" && !hasDeadline) {
      return false;
    }

    if (conditions.task_deadline_state === "no_deadline" && hasDeadline) {
      return false;
    }

    if (conditions.task_deadline_state === "overdue" && !isOverdue) {
      return false;
    }
  }

  if (conditions.task_tag_id) {
    if (!task) {
      return false;
    }

    const matchesTag = taskTags.some((tag) => tag.id === conditions.task_tag_id);
    if (!matchesTag) {
      return false;
    }
  }

  if (conditions.project_id) {
    const matchingProjectId =
      (typeof task?.project_id === "string" && task.project_id) ||
      (typeof note?.project_id === "string" && note.project_id) ||
      (typeof project?.id === "string" && project.id) ||
      null;

    if (matchingProjectId !== conditions.project_id) {
      return false;
    }
  }

  if (conditions.goal_id) {
    const matchingGoalId =
      (typeof task?.goal_id === "string" && task.goal_id) ||
      (typeof project?.goal_id === "string" && project.goal_id) ||
      null;

    if (matchingGoalId !== conditions.goal_id) {
      return false;
    }
  }

  if (conditions.assignee_id) {
    if (!task || task.assignee_id !== conditions.assignee_id) {
      return false;
    }
  }

  if (conditions.reviewer_id) {
    if (!task || task.reviewer_id !== conditions.reviewer_id) {
      return false;
    }
  }

  return true;
}

async function findConnectionForAction(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  provider: IntegrationProvider,
  actionConfig: AutomationActionConfig
) {
  const connectionId = actionConfig.connection_id;
  if (!connectionId) {
    throw new Error("This automation step needs a connected integration.");
  }

  const { data: connection } = await admin
    .from("integration_connections")
    .select(
      "id, user_id, team_id, provider, name, config, event_types, is_active, last_delivery_at, last_response_status, last_error, created_at, updated_at, secret_encrypted"
    )
    .eq("id", connectionId)
    .eq("provider", provider)
    .eq("is_active", true)
    .maybeSingle();

  const typedConnection =
    (connection as (IntegrationConnection & { secret_encrypted: string }) | null) || null;

  if (!typedConnection) {
    throw new Error("The selected integration connection could not be found.");
  }

  const sameWorkspace = rule.team_id
    ? typedConnection.team_id === rule.team_id
    : typedConnection.team_id === null && typedConnection.user_id === rule.user_id;

  if (!sameWorkspace) {
    throw new Error("The selected integration belongs to a different workspace.");
  }

  return typedConnection;
}

async function logAutomationCreatedTask(
  admin: ReturnType<typeof createAdminClient>,
  input: {
    rule: AutomationRule;
    envelope: WebhookEventEnvelope;
    taskId: string;
    taskTitle: string;
    projectId: string | null;
    action: string;
  }
) {
  await logProjectActivity(admin as never, {
    userId: input.rule.user_id,
    projectId: input.projectId,
    entityType: "task",
    entityId: input.taskId,
    action: input.action,
    title: input.taskTitle,
    details: {
      rule_id: input.rule.id,
      trigger_event: input.envelope.type,
    },
  });
  await logTeamAudit(admin as never, {
    teamId: input.rule.team_id,
    actorId: input.envelope.actor_id || input.rule.user_id,
    action: input.action,
    entityType: "task",
    entityId: input.taskId,
    title: input.taskTitle,
    details: {
      rule_id: input.rule.id,
      trigger_event: input.envelope.type,
    },
  });
}

async function logAutomationCreatedNote(
  admin: ReturnType<typeof createAdminClient>,
  input: {
    rule: AutomationRule;
    envelope: WebhookEventEnvelope;
    noteId: string;
    noteTitle: string;
    projectId: string | null;
    action: string;
  }
) {
  await logProjectActivity(admin as never, {
    userId: input.rule.user_id,
    projectId: input.projectId,
    entityType: "note",
    entityId: input.noteId,
    action: input.action,
    title: input.noteTitle,
    details: {
      rule_id: input.rule.id,
      trigger_event: input.envelope.type,
    },
  });
  await logTeamAudit(admin as never, {
    teamId: input.rule.team_id,
    actorId: input.envelope.actor_id || input.rule.user_id,
    action: input.action,
    entityType: "note",
    entityId: input.noteId,
    title: input.noteTitle,
    details: {
      rule_id: input.rule.id,
      trigger_event: input.envelope.type,
    },
  });
}

async function createFollowUpTask(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope,
  actionConfig: AutomationActionConfig
) {
  const sourceTitle = getPrimaryTitle(envelope.data) || "workspace item";
  const titleTemplate =
    actionConfig.task_title_template?.trim() || `Follow up: ${sourceTitle}`;
  const descriptionTemplate =
    actionConfig.task_description_template?.trim() ||
    `Generated by automation "${rule.name}" from {{event.type}}.`;
  const title = renderTemplate(titleTemplate, envelope).trim() || `Follow up: ${sourceTitle}`;
  const description = renderTemplate(descriptionTemplate, envelope).trim();
  const projectId = getSourceProjectId(envelope);
  const goalId = getSourceGoalId(envelope);

  const { data, error } = await admin
    .from("tasks")
    .insert({
      user_id: rule.user_id,
      team_id: rule.team_id,
      project_id: projectId,
      goal_id: goalId,
      assignee_id: actionConfig.assignee_id || null,
      reviewer_id: actionConfig.reviewer_id || null,
      title,
      description: description || null,
      status: "todo",
      priority: actionConfig.task_priority || "none",
    })
    .select("id, title, project_id")
    .single();

  if (error) {
    throw new Error(error.message || "Follow-up task could not be created.");
  }

  const createdTask = data as { id: string; title: string; project_id: string | null };
  await logAutomationCreatedTask(admin, {
    rule,
    envelope,
    taskId: createdTask.id,
    taskTitle: createdTask.title,
    projectId: createdTask.project_id,
    action: "task_created_by_automation",
  });

  return `Created follow-up task "${createdTask.title}"`;
}

async function createFollowUpNote(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope,
  actionConfig: AutomationActionConfig
) {
  const sourceTitle = getPrimaryTitle(envelope.data) || "workspace item";
  const titleTemplate =
    actionConfig.note_title_template?.trim() || `Follow-up note: ${sourceTitle}`;
  const contentTemplate =
    actionConfig.note_content_template?.trim() ||
    `Captured by automation "${rule.name}" from {{event.type}}.`;
  const title =
    renderTemplate(titleTemplate, envelope).trim() || `Follow-up note: ${sourceTitle}`;
  const content =
    renderTemplate(contentTemplate, envelope).trim() ||
    `Captured by automation "${rule.name}" from ${envelope.type}.`;
  const projectId = getSourceProjectId(envelope);

  const { data, error } = await admin
    .from("notes")
    .insert({
      user_id: rule.user_id,
      team_id: rule.team_id,
      project_id: projectId,
      folder_id: actionConfig.note_folder_id || null,
      title,
      content: createTextDocument(content),
    })
    .select("id, title, project_id")
    .single();

  if (error) {
    throw new Error(error.message || "Follow-up note could not be created.");
  }

  const createdNote = data as { id: string; title: string; project_id: string | null };
  await logAutomationCreatedNote(admin, {
    rule,
    envelope,
    noteId: createdNote.id,
    noteTitle: createdNote.title,
    projectId: createdNote.project_id,
    action: "note_created_by_automation",
  });

  return `Created note "${createdNote.title}"`;
}

async function createAIFollowUpTask(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope,
  actionConfig: AutomationActionConfig
) {
  const sourceTitle = getPrimaryTitle(envelope.data) || "workspace item";
  const promptTemplate =
    actionConfig.prompt_template?.trim() ||
    "Write a concise follow-up task description for this workspace event.";
  const prompt = renderTemplate(promptTemplate, envelope).trim();
  const aiResult = await runAutomationAIText(rule.user_id, prompt, {
    activeTeamId: rule.team_id,
  });
  const titleTemplate =
    actionConfig.task_title_template?.trim() || `AI follow up: ${sourceTitle}`;
  const title = renderTemplate(titleTemplate, envelope).trim() || `AI follow up: ${sourceTitle}`;
  const projectId = getSourceProjectId(envelope);
  const goalId = getSourceGoalId(envelope);

  const { data, error } = await admin
    .from("tasks")
    .insert({
      user_id: rule.user_id,
      team_id: rule.team_id,
      project_id: projectId,
      goal_id: goalId,
      assignee_id: actionConfig.assignee_id || null,
      reviewer_id: actionConfig.reviewer_id || null,
      title,
      description: aiResult.text || "AI generated this follow-up task.",
      status: "todo",
      priority: actionConfig.task_priority || "none",
    })
    .select("id, title, project_id")
    .single();

  if (error) {
    throw new Error(error.message || "AI follow-up task could not be created.");
  }

  const createdTask = data as { id: string; title: string; project_id: string | null };
  await logAutomationCreatedTask(admin, {
    rule,
    envelope,
    taskId: createdTask.id,
    taskTitle: createdTask.title,
    projectId: createdTask.project_id,
    action: "ai_task_created_by_automation",
  });

  return `AI created task "${createdTask.title}"`;
}

async function createAINote(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope,
  actionConfig: AutomationActionConfig
) {
  const sourceTitle = getPrimaryTitle(envelope.data) || "workspace item";
  const promptTemplate =
    actionConfig.prompt_template?.trim() ||
    "Summarize this workspace event into a practical note.";
  const prompt = renderTemplate(promptTemplate, envelope).trim();
  const aiResult = await runAutomationAIText(rule.user_id, prompt, {
    activeTeamId: rule.team_id,
  });
  const titleTemplate =
    actionConfig.note_title_template?.trim() || `AI summary: ${sourceTitle}`;
  const title = renderTemplate(titleTemplate, envelope).trim() || `AI summary: ${sourceTitle}`;
  const projectId = getSourceProjectId(envelope);

  const { data, error } = await admin
    .from("notes")
    .insert({
      user_id: rule.user_id,
      team_id: rule.team_id,
      project_id: projectId,
      title,
      content: createTextDocument(aiResult.text || "AI generated this note."),
    })
    .select("id, title, project_id")
    .single();

  if (error) {
    throw new Error(error.message || "AI note could not be created.");
  }

  const createdNote = data as { id: string; title: string; project_id: string | null };
  await logAutomationCreatedNote(admin, {
    rule,
    envelope,
    noteId: createdNote.id,
    noteTitle: createdNote.title,
    projectId: createdNote.project_id,
    action: "ai_note_created_by_automation",
  });

  return `AI created note "${createdNote.title}"`;
}

async function createAutomationGmailDraft(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope,
  actionConfig: AutomationActionConfig
) {
  const sourceTitle = getPrimaryTitle(envelope.data) || "workspace item";
  const subjectTemplate =
    actionConfig.email_subject_template?.trim() || `Mindavio update: ${sourceTitle}`;
  const bodyTemplate =
    actionConfig.email_body_template?.trim() ||
    `A Mindavio automation noticed {{event.type}} for ${sourceTitle}.`;
  const toTemplate = actionConfig.email_to_template?.trim() || "";
  const subject =
    renderTemplate(subjectTemplate, envelope).trim() || `Mindavio update: ${sourceTitle}`;
  const body =
    renderTemplate(bodyTemplate, envelope).trim() ||
    `A Mindavio automation noticed ${envelope.type} for ${sourceTitle}.`;
  const to = toTemplate ? renderTemplate(toTemplate, envelope).trim() : null;
  const connection = await findConnectionForAction(admin, rule, "gmail", actionConfig);
  const result = await createGmailDraft(admin, connection, {
    eventType: envelope.type,
    payload: envelope,
    to,
    subject,
    body,
  });

  return `Created Gmail draft for ${result.recipient}`;
}

async function executeAutomationStep(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  step: AutomationStep,
  envelope: WebhookEventEnvelope
) {
  const actionType = step.action_type;

  if (actionType === "send_slack") {
    const connection = await findConnectionForAction(admin, rule, "slack", step.action_config);
    await deliverSlackIntegrationEvent(admin, connection, envelope);
    return `Sent to Slack: ${connection.name}`;
  }

  if (actionType === "send_telegram") {
    const connection = await findConnectionForAction(
      admin,
      rule,
      "telegram",
      step.action_config
    );
    await deliverTelegramIntegrationEvent(admin, connection, envelope);
    return `Sent to Telegram: ${connection.name}`;
  }

  if (actionType === "send_google_sheets") {
    const connection = await findConnectionForAction(
      admin,
      rule,
      "google_sheets",
      step.action_config
    );
    await deliverGoogleSheetsIntegrationEvent(admin, connection, envelope);
    return `Forwarded to Google Sheets: ${connection.name}`;
  }

  if (actionType === "send_notion") {
    const connection = await findConnectionForAction(admin, rule, "notion", step.action_config);
    await deliverNotionIntegrationEvent(admin, connection, envelope);
    return `Sent to Notion: ${connection.name}`;
  }

  if (actionType === "create_gmail_draft") {
    return createAutomationGmailDraft(admin, rule, envelope, step.action_config);
  }

  if (actionType === "create_task") {
    return createFollowUpTask(admin, rule, envelope, step.action_config);
  }

  if (actionType === "create_note") {
    return createFollowUpNote(admin, rule, envelope, step.action_config);
  }

  if (actionType === "ai_create_task") {
    return createAIFollowUpTask(admin, rule, envelope, step.action_config);
  }

  if (actionType === "ai_create_note") {
    return createAINote(admin, rule, envelope, step.action_config);
  }

  const unsupportedAction: never = actionType;
  throw new Error(`Unsupported automation action: ${unsupportedAction as AutomationActionType}`);
}

async function executeAutomationRule(
  admin: ReturnType<typeof createAdminClient>,
  rule: AutomationRule,
  envelope: WebhookEventEnvelope
) {
  const summaries: string[] = [];

  for (const step of getRuleSteps(rule)) {
    const summary = await executeAutomationStep(admin, rule, step, envelope);
    summaries.push(summary);
  }

  return summaries.join(" | ");
}

async function recordAutomationRun(
  admin: ReturnType<typeof createAdminClient>,
  input: {
    rule: AutomationRule;
    envelope: WebhookEventEnvelope;
    success: boolean;
    resultSummary?: string | null;
    error?: string | null;
    isScheduled?: boolean;
  }
) {
  await admin.from("automation_runs").insert({
    rule_id: input.rule.id,
    user_id: input.rule.user_id,
    team_id: input.rule.team_id,
    trigger_mode: input.rule.trigger_mode,
    trigger_event: input.envelope.type,
    payload: input.envelope,
    success: input.success,
    result_summary: input.resultSummary || null,
    error: input.error || null,
  });

  await admin
    .from("automation_rules")
    .update({
      last_run_at: new Date().toISOString(),
      last_scheduled_run_at: input.isScheduled ? new Date().toISOString() : input.rule.last_scheduled_run_at,
      last_run_status: input.success ? "success" : "failed",
      last_error: input.success ? null : input.error || "Automation failed",
    })
    .eq("id", input.rule.id);
}

function getTimeZoneParts(date: Date, timeZone: string) {
  const formatter = new Intl.DateTimeFormat("en-US", {
    timeZone,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    weekday: "short",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  });

  const parts = formatter.formatToParts(date);
  const get = (type: Intl.DateTimeFormatPartTypes) =>
    parts.find((part) => part.type === type)?.value || "";
  const weekdayToken = get("weekday");
  const weekdayMap: Record<string, number> = {
    Sun: 0,
    Mon: 1,
    Tue: 2,
    Wed: 3,
    Thu: 4,
    Fri: 5,
    Sat: 6,
  };

  return {
    year: Number(get("year")),
    month: Number(get("month")),
    day: Number(get("day")),
    hour: Number(get("hour")),
    minute: Number(get("minute")),
    weekday: weekdayMap[weekdayToken] ?? null,
  };
}

function isSameLocalDate(a: Date, b: Date, timeZone: string) {
  const aParts = getTimeZoneParts(a, timeZone);
  const bParts = getTimeZoneParts(b, timeZone);

  return (
    aParts.year === bParts.year &&
    aParts.month === bParts.month &&
    aParts.day === bParts.day
  );
}

function isScheduledRuleDue(rule: AutomationRule, now: Date) {
  if (
    rule.trigger_mode !== "schedule" ||
    !rule.schedule_frequency ||
    !rule.schedule_time ||
    !rule.schedule_timezone
  ) {
    return false;
  }

  const [hourString, minuteString] = rule.schedule_time.split(":");
  const targetHour = Number(hourString);
  const targetMinute = Number(minuteString);
  if (!Number.isFinite(targetHour) || !Number.isFinite(targetMinute)) {
    return false;
  }

  const localNow = getTimeZoneParts(now, rule.schedule_timezone);
  const currentMinutes = localNow.hour * 60 + localNow.minute;
  const targetMinutes = targetHour * 60 + targetMinute;

  if (currentMinutes < targetMinutes) {
    return false;
  }

  if (rule.schedule_frequency === "weekly") {
    if (localNow.weekday === null || rule.schedule_day_of_week === null) {
      return false;
    }
    if (localNow.weekday !== rule.schedule_day_of_week) {
      return false;
    }
  }

  const lastRun =
    rule.last_scheduled_run_at || rule.last_run_at
      ? new Date(rule.last_scheduled_run_at || rule.last_run_at || "")
      : null;

  if (lastRun && !Number.isNaN(lastRun.getTime())) {
    if (isSameLocalDate(lastRun, now, rule.schedule_timezone)) {
      return false;
    }
  }

  return true;
}

function buildScheduledEnvelope(rule: AutomationRule): WebhookEventEnvelope {
  return {
    id: `schedule_${rule.id}_${Date.now()}`,
    type: "schedule.triggered",
    created_at: new Date().toISOString(),
    workspace: {
      team_id: rule.team_id,
      user_id: rule.user_id,
    },
    actor_id: null,
    data: {
      schedule: {
        rule_id: rule.id,
        frequency: rule.schedule_frequency,
        time: rule.schedule_time,
        timezone: rule.schedule_timezone,
        day_of_week: rule.schedule_day_of_week,
      },
    },
  };
}

export async function processAutomationRules(
  admin: ReturnType<typeof createAdminClient>,
  envelope: WebhookEventEnvelope
) {
  let query = admin
    .from("automation_rules")
    .select("*")
    .eq("is_active", true)
    .eq("trigger_mode", "event")
    .eq("trigger_event", envelope.type)
    .order("created_at", { ascending: false });

  query = envelope.workspace.team_id
    ? query.eq("team_id", envelope.workspace.team_id)
    : query.eq("user_id", envelope.workspace.user_id).is("team_id", null);

  const { data, error } = await query;
  if (error || !data || data.length === 0) {
    return { matched: 0, executed: 0 };
  }

  const rules = (data as AutomationRule[]).filter((rule) =>
    matchesConditions(rule, envelope)
  );

  await Promise.allSettled(
    rules.map(async (rule) => {
      try {
        const resultSummary = await executeAutomationRule(admin, rule, envelope);
        await recordAutomationRun(admin, {
          rule,
          envelope,
          success: true,
          resultSummary,
        });
      } catch (error) {
        const message =
          error instanceof Error ? error.message : "Automation execution failed";
        await recordAutomationRun(admin, {
          rule,
          envelope,
          success: false,
          error: message,
        });
      }
    })
  );

  return {
    matched: rules.length,
    executed: rules.length,
  };
}

export async function processScheduledAutomationRules(
  admin: ReturnType<typeof createAdminClient>,
  input: {
    userId: string;
    teamId: string | null;
  }
) {
  let query = admin
    .from("automation_rules")
    .select("*")
    .eq("is_active", true)
    .eq("trigger_mode", "schedule")
    .order("created_at", { ascending: false });

  query = input.teamId
    ? query.eq("team_id", input.teamId)
    : query.eq("user_id", input.userId).is("team_id", null);

  const { data, error } = await query;
  if (error || !data || data.length === 0) {
    return { due: 0, executed: 0 };
  }

  const now = new Date();
  const dueRules = (data as AutomationRule[]).filter((rule) =>
    isScheduledRuleDue(rule, now)
  );

  await Promise.allSettled(
    dueRules.map(async (rule) => {
      const envelope = buildScheduledEnvelope(rule);

      try {
        const resultSummary = await executeAutomationRule(admin, rule, envelope);
        await recordAutomationRun(admin, {
          rule,
          envelope,
          success: true,
          resultSummary,
          isScheduled: true,
        });
      } catch (error) {
        const message =
          error instanceof Error ? error.message : "Scheduled automation failed";
        await recordAutomationRun(admin, {
          rule,
          envelope,
          success: false,
          error: message,
          isScheduled: true,
        });
      }
    })
  );

  return {
    due: dueRules.length,
    executed: dueRules.length,
  };
}
