"use client";

import { useState, useCallback, useEffect, useRef } from "react";
import {
  Plus,
  Heart,
  Clock,
  Play,
  Trash2,
  X,
  Power,
  ExternalLink,
  Pencil,
} from "lucide-react";
import { CustomSelect, type SelectOption } from "@/components/ui/custom-select";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Input, Textarea } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { cn } from "@/lib/utils";
import { useI18n } from "@/components/providers/i18n-provider";
import { useCronTasks } from "@/hooks/use-cron-tasks";
import { useTaskExecutions } from "@/hooks/use-task-executions";
import { useImChannels } from "@/hooks/use-im-channels";
import type { CronTask, TaskActionType } from "@/lib/types";

/* ─── Frequency → Cron helpers ─── */
type FreqUnit = "once" | "minutes" | "hourly" | "daily" | "weekly" | "monthly";

interface VisualSchedule {
  unit: FreqUnit;
  interval: number; // e.g. 5, 15, 30 for "minutes"
  time: string; // HH:MM for daily/weekly/monthly/once
  dayOfWeek: number; // 0-6 for weekly (0=Sun)
  dayOfMonth: number; // 1-31 for monthly
  onceDate: string; // YYYY-MM-DD for once
}

const DEFAULT_SCHEDULE: VisualSchedule = {
  unit: "daily",
  interval: 30,
  time: "09:00",
  dayOfWeek: 1,
  dayOfMonth: 1,
  onceDate: new Date().toISOString().slice(0, 10),
};

function scheduleToCron(s: VisualSchedule): string {
  switch (s.unit) {
    case "once": {
      // Store as a specific date+time cron. The engine will disable the task after execution.
      const [h, m] = s.time.split(":").map(Number);
      const d = new Date(s.onceDate);
      return `${m || 0} ${h || 9} ${d.getDate()} ${d.getMonth() + 1} *`;
    }
    case "minutes":
      return `*/${s.interval} * * * *`;
    case "hourly":
      return "0 * * * *";
    case "daily": {
      const [h, m] = s.time.split(":").map(Number);
      return `${m || 0} ${h || 9} * * *`;
    }
    case "weekly": {
      const [h, m] = s.time.split(":").map(Number);
      return `${m || 0} ${h || 9} * * ${s.dayOfWeek}`;
    }
    case "monthly": {
      const [h, m] = s.time.split(":").map(Number);
      return `${m || 0} ${h || 9} ${s.dayOfMonth} * *`;
    }
  }
}

function cronToHumanLabel(cron: string, t: (key: string) => string): string {
  if (!cron) return "—";
  const parts = cron.split(/\s+/);
  if (parts.length !== 5) return cron;

  const [min, hour, dom, month, dow] = parts;

  if (min.startsWith("*/"))
    return t("schedule.everyNMin").replace("{n}", min.slice(2));
  if (hour === "*" && min === "0") return t("schedule.everyHour");
  const hh = hour.padStart(2, "0");
  const mm = min.padStart(2, "0");
  if (dow !== "*") {
    const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
    return `${days[Number(dow)] || dow} ${hh}:${mm}`;
  }
  // Once: specific day+month (dom and month are not *)
  if (dom !== "*" && month !== "*")
    return `${t("schedule.once")} ${month}/${dom} ${hh}:${mm}`;
  if (dom !== "*")
    return t("schedule.day")
      .replace("{dom}", dom)
      .replace("{time}", `${hh}:${mm}`);
  return t("schedule.everyDay").replace("{time}", `${hh}:${mm}`);
}

const MINUTE_OPTIONS: SelectOption[] = [5, 10, 15, 20, 30, 45].map((n) => ({
  value: String(n),
  label: `${n} min`,
}));
const DOW_OPTIONS: SelectOption[] = [
  "Sunday",
  "Monday",
  "Tuesday",
  "Wednesday",
  "Thursday",
  "Friday",
  "Saturday",
].map((d, i) => ({ value: String(i), label: d }));
const DOM_OPTIONS: SelectOption[] = Array.from({ length: 28 }, (_, i) => ({
  value: String(i + 1),
  label: String(i + 1),
}));

const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0"));
const MINUTES = Array.from({ length: 60 }, (_, i) =>
  String(i).padStart(2, "0"),
);

/* ─── Custom Time Picker ─── */
function TimePicker({
  value,
  onChange,
}: {
  value: string;
  onChange: (v: string) => void;
}) {
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);
  const [hh, mm] = value.split(":");
  const hourRef = useRef<HTMLDivElement>(null);
  const minRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (ref.current && !ref.current.contains(e.target as Node))
        setOpen(false);
    };
    document.addEventListener("mousedown", handler);
    return () => document.removeEventListener("mousedown", handler);
  }, []);

  // Scroll selected items into view when dropdown opens
  useEffect(() => {
    if (!open) return;
    setTimeout(() => {
      hourRef.current
        ?.querySelector('[data-selected="true"]')
        ?.scrollIntoView({ block: "center" });
      minRef.current
        ?.querySelector('[data-selected="true"]')
        ?.scrollIntoView({ block: "center" });
    }, 0);
  }, [open]);

  return (
    <div ref={ref} className="relative w-[120px]">
      <button
        type="button"
        onClick={() => setOpen(!open)}
        className={cn(
          "w-full h-10 px-3 rounded-lg bg-surface-2 border text-[14px] text-ink flex items-center justify-between transition-colors focus:outline-none focus-visible:shadow-focus-ring",
          open ? "border-brand-primary" : "border-hairline hover:border-hairline-strong",
        )}
      >
        <span>{value}</span>
        <Clock size={16} className="text-ink-tertiary" />
      </button>
      {open && (
        <div className="absolute top-[calc(100%+4px)] left-0 w-[120px] rounded-lg bg-surface-1 border border-hairline z-50 flex overflow-hidden shadow-forge-lg">
          <div
            ref={hourRef}
            className="flex-1 max-h-[180px] overflow-y-auto scrollbar-thin"
          >
            {HOURS.map((h) => (
              <button
                key={h}
                type="button"
                data-selected={h === hh}
                onClick={() => {
                  onChange(`${h}:${mm}`);
                }}
                className={cn(
                  "w-full h-8 text-[13px] flex items-center justify-center",
                  h === hh
                    ? "bg-brand-primary text-on-primary font-semibold"
                    : "text-ink-tertiary hover:bg-surface-2 hover:text-ink-muted",
                )}
              >
                {h}
              </button>
            ))}
          </div>
          <div className="w-px bg-hairline" />
          <div
            ref={minRef}
            className="flex-1 max-h-[180px] overflow-y-auto scrollbar-thin"
          >
            {MINUTES.map((m) => (
              <button
                key={m}
                type="button"
                data-selected={m === mm}
                onClick={() => {
                  onChange(`${hh}:${m}`);
                }}
                className={cn(
                  "w-full h-8 text-[13px] flex items-center justify-center",
                  m === mm
                    ? "bg-brand-primary text-on-primary font-semibold"
                    : "text-ink-tertiary hover:bg-surface-2 hover:text-ink-muted",
                )}
              >
                {m}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

/* ─── Main component ─── */

interface ScheduleViewProps {
  workspaceId: string;
  mode?: "schedule" | "webhook";
}

export function ScheduleView({
  workspaceId,
  mode = "schedule",
}: ScheduleViewProps) {
  const { t } = useI18n();
  const isScheduleMode = mode === "schedule";
  const {
    tasks,
    heartbeat,
    cronTasks,
    updateTask,
    createTask,
    deleteTask,
    executeTask,
  } = useCronTasks(workspaceId);
  const {
    executions,
    refreshExecutions,
    loadMore,
    hasMore,
    loadingMore,
    typeFilter,
    statusFilter,
    setTypeFilter,
    setStatusFilter,
  } = useTaskExecutions(workspaceId)
  const { channels: imChannels } = useImChannels(workspaceId);

  const channelOptions: SelectOption[] = imChannels
    .filter((c) => c.status === "connected")
    .map((c) => ({ value: c.id, label: c.name }));
  const [showNewForm, setShowNewForm] = useState(false);
  const [editingTask, setEditingTask] = useState<CronTask | null>(null);
  const [showChecklist, setShowChecklist] = useState(false);
  const [engineRunning, setEngineRunning] = useState(false);
  const [engineLoading, setEngineLoading] = useState(false);

  useEffect(() => {
    if (!isScheduleMode) return;
    fetch("/api/cron-engine")
      .then((r) => r.json())
      .then((d) => setEngineRunning(d.running))
      .catch(() => {});
  }, [isScheduleMode]);

  const toggleEngine = useCallback(async () => {
    setEngineLoading(true);
    try {
      const res = await fetch("/api/cron-engine", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ action: engineRunning ? "stop" : "start" }),
      });
      const data = await res.json();
      setEngineRunning(data.running);
    } catch {
      /* ignore */
    }
    setEngineLoading(false);
  }, [engineRunning]);

  const handleExecuteHeartbeat = useCallback(async () => {
    if (!heartbeat) return;
    await executeTask(heartbeat.id);
    refreshExecutions();
    window.dispatchEvent(new CustomEvent("forge:sessions-changed"));
  }, [heartbeat, executeTask, refreshExecutions]);

  const handleToggleTask = useCallback(
    async (id: string, enabled: boolean) => {
      await updateTask(id, { enabled });
    },
    [updateTask],
  );

  const handleDeleteTask = useCallback(
    async (id: string) => {
      await deleteTask(id);
    },
    [deleteTask],
  );

  const handleCreateTask = useCallback(
    async (opts: Parameters<typeof createTask>[0]) => {
      await createTask(opts);
      setShowNewForm(false);
    },
    [createTask],
  );

  const handleEditTask = useCallback(
    async (id: string, opts: Record<string, unknown>) => {
      await updateTask(id, opts);
      setEditingTask(null);
    },
    [updateTask],
  );

  const actionLabel = (task: {
    actionType: string;
    agentName: string;
    skillName: string;
    action: string;
  }) => {
    switch (task.actionType) {
      case "run-agent":
        return `${t("schedule.actionType.runAgent")}: ${task.agentName || "—"}`;
      case "run-skill":
        return `${t("schedule.actionType.runSkill")}: ${task.skillName || "—"}`;
      default:
        return t("schedule.actionType.customPrompt");
    }
  };

  const INTERVALS = [
    { value: "5m", label: "5 Minutes" },
    { value: "15m", label: "15 Minutes" },
    { value: "30m", label: "30 Minutes" },
    { value: "1h", label: "1 Hour" },
    { value: "6h", label: "6 Hours" },
    { value: "24h", label: "24 Hours" },
  ];

  return (
    <div className="flex flex-col h-full">
      {/* Header */}
      <div className="flex items-center justify-between h-[52px] px-6 border-b border-hairline shrink-0">
        <div className="flex items-center gap-3">
          <span className="text-[20px] font-semibold text-ink font-heading tracking-tight">
            {isScheduleMode ? t("schedule.title") : t("schedule.webhookTitle")}
          </span>
          {isScheduleMode && (
            <button
              onClick={toggleEngine}
              disabled={engineLoading}
              className={cn(
                "flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[11px] font-medium transition-colors",
                engineRunning
                  ? "bg-green/10 text-green hover:bg-green/20"
                  : "bg-surface-3 text-ink-tertiary hover:bg-surface-2",
              )}
            >
              <Power size={12} />
              {engineLoading
                ? "..."
                : engineRunning
                  ? t("schedule.engineRunning")
                  : t("schedule.engineStopped")}
            </button>
          )}
        </div>
        <Button
          variant="primary"
          size="sm"
          leftIcon={<Plus size={14} />}
          onClick={() => setShowNewForm(true)}
        >
          {isScheduleMode
            ? t("schedule.newTask")
            : t("schedule.newWebhookTask")}
        </Button>
      </div>

      {/* Content */}
      <div className="flex-1 overflow-y-auto px-8 py-6 space-y-6">
        {/* Heartbeat Section — only in schedule mode */}
        {isScheduleMode && heartbeat && (
          <Card>
            <div className="flex items-center justify-between mb-4">
              <div className="flex items-center gap-2">
                <Heart size={16} className="text-coral" />
                <span className="text-[14px] font-semibold text-ink">
                  {t("schedule.heartbeat")}
                </span>
              </div>
              <div className="flex items-center gap-2">
                <span
                  className={cn(
                    "text-[12px]",
                    heartbeat.enabled ? "text-green" : "text-ink-tertiary",
                  )}
                >
                  {heartbeat.enabled
                    ? t("schedule.enabled")
                    : t("schedule.disabled")}
                </span>
                <Switch
                  size="sm"
                  checked={heartbeat.enabled}
                  onChange={() =>
                    updateTask(heartbeat.id, { enabled: !heartbeat.enabled })
                  }
                  aria-label={t("schedule.heartbeat")}
                />
              </div>
            </div>
            <div className="grid grid-cols-3 gap-4">
              <div>
                <label className="block text-[12px] text-ink-muted mb-1.5">
                  {t("schedule.checkInterval")}
                </label>
                <CustomSelect
                  value={heartbeat.config.check_interval || "30m"}
                  onChange={(v) => {
                    // Also sync the cron schedule expression
                    const cronMap: Record<string, string> = {
                      "5m": "*/5 * * * *",
                      "15m": "*/15 * * * *",
                      "30m": "*/30 * * * *",
                      "1h": "0 * * * *",
                      "6h": "0 */6 * * *",
                      "24h": "0 9 * * *",
                    };
                    updateTask(heartbeat.id, {
                      config: { ...heartbeat.config, check_interval: v },
                      schedule: cronMap[v] || "*/30 * * * *",
                    });
                  }}
                  options={INTERVALS.map((i) => ({
                    value: i.value,
                    label: i.label,
                  }))}
                  size="sm"
                />
              </div>
              <div>
                <label className="block text-[12px] text-ink-muted mb-1.5">
                  {t("schedule.notifyChannel")}
                </label>
                <CustomSelect
                  value={heartbeat.config.notify_channel || ""}
                  onChange={(v) =>
                    updateTask(heartbeat.id, {
                      config: { ...heartbeat.config, notify_channel: v },
                    })
                  }
                  options={[
                    { value: "", label: t("schedule.notifyNone") },
                    ...channelOptions,
                  ]}
                  size="sm"
                />
              </div>
              <div>
                <label className="block text-[12px] text-ink-muted mb-1.5">
                  HEARTBEAT.md
                </label>
                <Button
                  variant="secondary"
                  fullWidth
                  onClick={() => setShowChecklist(true)}
                >
                  {t("schedule.editChecklist")}
                </Button>
              </div>
            </div>
            <div className="mt-3 flex items-center justify-between gap-3">
              <div className="flex items-center gap-2 text-[11px] text-ink-tertiary">
                <Clock size={12} className="shrink-0" />
                <span>
                  {t("schedule.lastRun")}{" "}
                  {heartbeat.lastRunAt
                    ? new Date(heartbeat.lastRunAt).toLocaleString()
                    : t("schedule.never")}
                </span>
                {heartbeat.lastRunResult && (
                  <span
                    className={cn(
                      "px-1.5 py-0.5 rounded text-[10px] font-medium",
                      heartbeat.lastRunResult.includes("error") ||
                        heartbeat.lastRunResult.includes("fail")
                        ? "bg-coral/15 text-coral"
                        : "bg-green/15 text-green",
                    )}
                  >
                    {heartbeat.lastRunResult.includes("error") ||
                    heartbeat.lastRunResult.includes("fail")
                      ? t("schedule.failed")
                      : t("schedule.success")}
                  </span>
                )}
              </div>
              <Button
                variant="secondary"
                size="sm"
                leftIcon={<Play size={11} />}
                onClick={handleExecuteHeartbeat}
                className="shrink-0 whitespace-nowrap"
              >
                {t("schedule.checkNow")}
              </Button>
            </div>
          </Card>
        )}

        {/* Tasks Table — schedule or webhook */}
        <Card>
          <h2 className="text-[14px] font-semibold text-ink mb-3">
            {isScheduleMode
              ? t("schedule.cronTasks")
              : t("schedule.webhookTitle")}
          </h2>
          <table className="w-full">
            <thead>
              <tr className="border-b border-hairline">
                <th className="text-left text-[11px] font-medium text-ink-tertiary pb-2 w-8"></th>
                <th className="text-left text-[11px] font-medium text-ink-tertiary pb-2 w-[160px]">
                  {t("table.name")}
                </th>
                <th className="text-left text-[11px] font-medium text-ink-tertiary pb-2 w-[160px]">
                  {isScheduleMode
                    ? t("table.schedule")
                    : t("table.triggerMethod")}
                </th>
                <th className="text-left text-[11px] font-medium text-ink-tertiary pb-2">
                  {t("table.action")}
                </th>
                <th className="text-right text-[11px] font-medium text-ink-tertiary pb-2 w-[100px]"></th>
              </tr>
            </thead>
            <tbody>
              {(isScheduleMode
                ? cronTasks
                : tasks.filter((t) => t.isWebhook)
              ).map((task) => (
                <tr
                  key={task.id}
                  className="border-b border-hairline last:border-0 group"
                >
                  <td className="py-2.5">
                    <div
                      className={cn(
                        "w-2 h-2 rounded-full",
                        task.enabled ? "bg-green" : "bg-muted",
                      )}
                    />
                  </td>
                  <td className="py-2.5 text-[13px] text-ink">
                    {task.name}
                  </td>
                  <td className="py-2.5 text-[12px] text-ink-muted">
                    {isScheduleMode ? (
                      cronToHumanLabel(task.schedule, t)
                    ) : (
                      <div className="flex flex-col gap-1">
                        <span className="px-1.5 py-0.5 rounded text-[10px] bg-primary-subtle text-brand-primary w-fit">
                          {t("sidebar.webhook")}
                        </span>
                        {task.id && (
                          <code className="text-[10px] font-mono text-ink-tertiary truncate">
                            {typeof window !== "undefined"
                              ? `${window.location.origin}/api/webhooks/${task.id}`
                              : `/api/webhooks/${task.id}`}
                          </code>
                        )}
                      </div>
                    )}
                  </td>
                  <td className="py-2.5 text-[12px] text-ink-muted">
                    {actionLabel(task)}
                  </td>
                  <td className="py-2.5">
                    <div className="flex items-center justify-end gap-2">
                      <Switch
                        size="sm"
                        checked={task.enabled}
                        onChange={() => handleToggleTask(task.id, !task.enabled)}
                        aria-label={task.name}
                      />
                      <button
                        onClick={() => setEditingTask(task)}
                        className="opacity-0 group-hover:opacity-100 transition-opacity"
                      >
                        <Pencil
                          size={13}
                          className="text-ink-tertiary hover:text-brand-primary"
                        />
                      </button>
                      <button
                        onClick={() => handleDeleteTask(task.id)}
                        className="opacity-0 group-hover:opacity-100 transition-opacity"
                      >
                        <Trash2
                          size={13}
                          className="text-ink-tertiary hover:text-coral"
                        />
                      </button>
                    </div>
                  </td>
                </tr>
              ))}
              {(isScheduleMode ? cronTasks : tasks.filter((t) => t.isWebhook))
                .length === 0 && (
                <tr>
                  <td
                    colSpan={5}
                    className="py-6 text-center text-[12px] text-ink-tertiary"
                  >
                    {isScheduleMode
                      ? t("schedule.noCronTasks")
                      : t("schedule.noWebhookTasks")}
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </Card>

        {/* Execution History */}
        <Card>
          <div className="flex items-center justify-between mb-3">
            <h2 className="text-[14px] font-semibold text-ink">
              {t("schedule.executionHistory")}
            </h2>
            <div className="flex items-center gap-2">
              <CustomSelect
                value={typeFilter}
                onChange={setTypeFilter}
                options={[
                  { value: "all", label: "All Types" },
                  ...(isScheduleMode
                    ? [
                        { value: "heartbeat", label: "Heartbeat" },
                        { value: "cron", label: "Cron Tasks" },
                      ]
                    : [{ value: "webhook", label: "Webhook" }]),
                ]}
                size="sm"
              />
              <CustomSelect
                value={statusFilter}
                onChange={setStatusFilter}
                options={[
                  { value: "all", label: "All Status" },
                  { value: "ok", label: "✓ OK" },
                  { value: "alert", label: "⚠ Alert" },
                  { value: "error", label: "✗ Error" },
                ]}
                size="sm"
              />
            </div>
          </div>
          {executions.length > 0 ? (
            <div className="space-y-1">
              {executions.map((exec) => (
                <div
                  key={exec.id}
                  className="flex items-center justify-between px-2 py-1.5 rounded hover:bg-surface-2 transition-colors"
                >
                  <div className="flex items-center gap-3">
                    <span className="text-[11px] text-ink-tertiary font-mono w-[120px] shrink-0">
                      {exec.executedAt?.slice(5, 16).replace("T", " ") || ""}
                    </span>
                    <span className="text-[12px] text-ink-muted w-[120px] shrink-0">
                      {exec.taskName}
                    </span>
                    <span
                      className={cn(
                        "text-[12px]",
                        exec.status === "ok"
                          ? "text-green"
                          : exec.status === "alert"
                            ? "text-amber"
                            : "text-coral",
                      )}
                    >
                      {exec.status === "ok"
                        ? "✓"
                        : exec.status === "alert"
                          ? "⚠"
                          : "✗"}{" "}
                      {exec.result?.slice(0, 60)}
                    </span>
                  </div>
                  {exec.sessionId && (
                    <button
                      onClick={() => {
                        window.dispatchEvent(
                          new CustomEvent("forge:navigate-session", {
                            detail: { sessionId: exec.sessionId },
                          }),
                        );
                      }}
                      className="flex items-center gap-1 text-[12px] text-indigo hover:underline shrink-0"
                    >
                      <ExternalLink size={11} /> {t("schedule.viewSession")}
                    </button>
                  )}
                </div>
              ))}
              {hasMore && (
                <div className="flex justify-center pt-3">
                  <Button
                    variant="secondary"
                    size="sm"
                    onClick={loadMore}
                    disabled={loadingMore}
                    loading={loadingMore}
                  >
                    {loadingMore ? "..." : "Load More"}
                  </Button>
                </div>
              )}
            </div>
          ) : (
            <p className="text-[12px] text-ink-tertiary text-center py-4">
              {t("schedule.noExecutionHistory")}
            </p>
          )}
        </Card>
      </div>

      {/* Checklist Editor Modal */}
      {showChecklist && (
        <EditChecklistModal
          workspaceId={workspaceId}
          onClose={() => setShowChecklist(false)}
        />
      )}

      {/* New Task Modal */}
      {showNewForm && (
        <TaskFormModal
          mode="create"
          viewMode={mode}
          workspaceId={workspaceId}
          channelOptions={channelOptions}
          onSubmit={async (opts) => {
            await handleCreateTask(opts);
          }}
          onClose={() => setShowNewForm(false)}
        />
      )}

      {/* Edit Task Modal */}
      {editingTask && (
        <TaskFormModal
          mode="edit"
          viewMode={mode}
          workspaceId={workspaceId}
          task={editingTask}
          channelOptions={channelOptions}
          onSubmit={async (opts) => {
            await handleEditTask(editingTask.id, opts);
          }}
          onClose={() => setEditingTask(null)}
        />
      )}
    </div>
  );
}

/* ─── Edit Checklist Modal ─── */

function EditChecklistModal({
  workspaceId,
  onClose,
}: {
  workspaceId: string;
  onClose: () => void;
}) {
  const { t } = useI18n();
  const [content, setContent] = useState("");
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);

  useEffect(() => {
    fetch(`/api/workspaces/${workspaceId}/files?name=HEARTBEAT.md`)
      .then((r) => (r.ok ? r.json() : { content: "" }))
      .then((d) => setContent(d.content || ""))
      .catch(() => setContent(""))
      .finally(() => setLoading(false));
  }, [workspaceId]);

  const handleSave = async () => {
    setSaving(true);
    try {
      await fetch(`/api/workspaces/${workspaceId}/files`, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ name: "HEARTBEAT.md", content }),
      });
      onClose();
    } catch {
      /* ignore */
    }
    setSaving(false);
  };

  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 animate-fade-in">
      <Card className="w-[560px] max-h-[80vh] flex flex-col animate-scale-in">
        <div className="flex items-center justify-between mb-4">
          <span className="text-[14px] font-semibold text-ink">
            {t("schedule.editHeartbeat")}
          </span>
          <button onClick={onClose} className="text-ink-tertiary hover:text-ink-muted">
            <X size={16} />
          </button>
        </div>
        {loading ? (
          <p className="text-[12px] text-ink-tertiary py-4">{t("common.loading")}</p>
        ) : (
          <textarea
            value={content}
            onChange={(e) => setContent(e.target.value)}
            className="flex-1 min-h-[300px] w-full px-3 py-2 rounded-lg bg-surface-2 border border-hairline text-[13px] text-ink font-mono outline-none focus:border-brand-primary resize-none"
            placeholder="# Heartbeat Checklist&#10;&#10;- [ ] Check disk space&#10;- [ ] Check API health&#10;- [ ] Check error logs"
          />
        )}
        <div className="flex justify-end gap-2 pt-3">
          <Button variant="secondary" size="sm" onClick={onClose}>
            {t("common.cancel")}
          </Button>
          <Button
            variant="primary"
            size="sm"
            onClick={handleSave}
            disabled={saving}
            loading={saving}
          >
            {saving ? t("status.saving") : t("common.save")}
          </Button>
        </div>
      </Card>
    </div>
  );
}

/* ─── Cron → Visual Schedule reverse parser ─── */

function cronToSchedule(cron: string): VisualSchedule {
  const s = { ...DEFAULT_SCHEDULE };
  if (!cron) return s;
  const parts = cron.split(/\s+/);
  if (parts.length !== 5) return s;
  const [min, hour, dom, , dow] = parts;

  if (min.startsWith("*/")) {
    s.unit = "minutes";
    s.interval = parseInt(min.slice(2), 10) || 30;
  } else if (hour === "*" && min === "0") {
    s.unit = "hourly";
  } else if (dow !== "*") {
    s.unit = "weekly";
    s.dayOfWeek = parseInt(dow, 10) || 0;
    s.time = `${hour.padStart(2, "0")}:${min.padStart(2, "0")}`;
  } else if (dom !== "*") {
    s.unit = "monthly";
    s.dayOfMonth = parseInt(dom, 10) || 1;
    s.time = `${hour.padStart(2, "0")}:${min.padStart(2, "0")}`;
  } else {
    s.unit = "daily";
    s.time = `${hour.padStart(2, "0")}:${min.padStart(2, "0")}`;
  }
  return s;
}

/* ─── Task Form Modal (Create + Edit) ─── */

function TaskFormModal({
  mode,
  workspaceId,
  task,
  onSubmit,
  onClose,
  viewMode = "schedule",
  channelOptions,
}: {
  mode: "create" | "edit";
  workspaceId: string;
  task?: CronTask;
  viewMode?: "schedule" | "webhook";
  channelOptions: SelectOption[];
  onSubmit: (opts: {
    name: string;
    schedule?: string;
    action?: string;
    action_type?: TaskActionType;
    agent_name?: string;
    skill_name?: string;
    workspace_id?: string;
    config?: Record<string, string>;
    webhook_enabled?: boolean;
  }) => Promise<void>;
  onClose: () => void;
}) {
  const { t } = useI18n();
  const [name, setName] = useState(task?.name || "");
  const [schedule, setSchedule] = useState<VisualSchedule>(
    task ? cronToSchedule(task.schedule) : { ...DEFAULT_SCHEDULE },
  );
  const [actionType, setActionType] = useState<TaskActionType>(
    task?.actionType || "run-agent",
  );
  const [agentName, setAgentName] = useState(task?.agentName || "");
  const [skillName, setSkillName] = useState(task?.skillName || "");
  const [notifyChannel, setNotifyChannel] = useState(
    task?.config.notify_channel || "",
  );
  const [description, setDescription] = useState(task?.action || "");
  const [submitting, setSubmitting] = useState(false);
  const isScheduleForm = viewMode === "schedule";

  // Fetch available agents for this workspace
  const [agents, setAgents] = useState<SelectOption[]>([]);
  const [skills, setSkills] = useState<SelectOption[]>([]);

  const FREQ_OPTIONS: SelectOption[] = [
    { value: "once", label: t("schedule.frequency.once") },
    { value: "minutes", label: t("schedule.frequency.minutes") },
    { value: "hourly", label: t("schedule.frequency.hourly") },
    { value: "daily", label: t("schedule.frequency.daily") },
    { value: "weekly", label: t("schedule.frequency.weekly") },
    { value: "monthly", label: t("schedule.frequency.monthly") },
  ];

  const ACTION_OPTIONS: SelectOption[] = [
    { value: "run-agent", label: t("schedule.actionType.runAgent") },
    { value: "run-skill", label: t("schedule.actionType.runSkill") },
    { value: "custom-prompt", label: t("schedule.actionType.customPrompt") },
  ];

  useEffect(() => {
    if (!workspaceId) return;
    // Fetch project-level agents
    fetch(`/api/workspaces/${encodeURIComponent(workspaceId)}/agents`)
      .then((r) => (r.ok ? r.json() : []))
      .then((data) => {
        const items: SelectOption[] = (Array.isArray(data) ? data : []).map(
          (a: { filename: string; name: string }) => ({
            value: a.filename.replace(/\.md$/, ""),
            label: `${a.name || a.filename} (Project)`,
          }),
        );
        // Also fetch global agents
        return fetch("/api/workspaces/__global__/agents")
          .then((r) => (r.ok ? r.json() : []))
          .then((gData) => {
            const gItems: SelectOption[] = (
              Array.isArray(gData) ? gData : []
            ).map((a: { filename: string; name: string }) => ({
              value: `global:${a.filename.replace(/\.md$/, "")}`,
              label: `${a.name || a.filename} (Global)`,
            }));
            setAgents([...items, ...gItems]);
          });
      })
      .catch(() => setAgents([]));
  }, [workspaceId]);

  useEffect(() => {
    // Fetch skills from current workspace's skill tree
    if (!workspaceId) return;
    fetch(`/api/workspaces/${encodeURIComponent(workspaceId)}/skills-tree`)
      .then((r) => (r.ok ? r.json() : { tree: [] }))
      .then((data) => {
        const items: SelectOption[] = [];
        const walk = (nodes: { name: string; type: string; children?: any[] }[]) => {
          for (const n of nodes) {
            if (n.type === "folder" && n.children?.some((c) => c.name === "SKILL.md")) {
              items.push({ value: n.name, label: n.name });
            }
            if (n.children) walk(n.children);
          }
        };
        walk(data.tree || []);
        setSkills(items);
      })
      .catch(() => setSkills([]));
  }, [workspaceId]);

  const handleSubmit = async () => {
    if (!name.trim()) return;
    setSubmitting(true);
    try {
      await onSubmit({
        name: name.trim(),
        ...(isScheduleForm ? { schedule: scheduleToCron(schedule) } : {}),
        action: description,
        action_type: actionType,
        agent_name:
          actionType === "run-agent" ? agentName.replace(/^global:/, "") : "",
        skill_name: actionType === "run-skill" ? skillName : "",
        workspace_id: workspaceId,
        config: {
          notify_channel: notifyChannel || "",
          description,
        },
        webhook_enabled: !isScheduleForm,
      });
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 animate-fade-in">
      <Card padded={false} className="w-[500px] flex flex-col animate-scale-in">
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-5 border-b border-hairline">
          <span className="text-[18px] font-semibold text-ink">
            {viewMode === "webhook"
              ? mode === "edit"
                ? t("schedule.editWebhookTaskTitle")
                : t("schedule.newWebhookTaskTitle")
              : mode === "edit"
                ? t("schedule.editTaskTitle")
                : t("schedule.newTaskTitle")}
          </span>
          <button onClick={onClose} className="text-ink-tertiary hover:text-ink-muted">
            <X size={18} />
          </button>
        </div>

        {/* Body */}
        <div className="px-6 py-5 space-y-4">
          {/* Task Name */}
          <div>
            <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
              {t("form.taskName")}
            </label>
            <Input
              inputSize="lg"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder={t("form.taskNamePlaceholder")}
              suppressAutofill
              autoFocus
              className="w-full"
            />
          </div>

          {/* Frequency (Visual Schedule Selector) — only in schedule mode */}
          {isScheduleForm && (
            <div>
              <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
                {t("form.frequency")}
              </label>
              <div className="flex gap-2">
                <div className="flex-1">
                  <CustomSelect
                    value={schedule.unit}
                    onChange={(v) =>
                      setSchedule((s) => ({ ...s, unit: v as FreqUnit }))
                    }
                    options={FREQ_OPTIONS}
                    size="md"
                  />
                </div>
                {schedule.unit === "minutes" && (
                  <div className="w-[100px]">
                    <CustomSelect
                      value={String(schedule.interval)}
                      onChange={(v) =>
                        setSchedule((s) => ({ ...s, interval: Number(v) }))
                      }
                      options={MINUTE_OPTIONS}
                      size="md"
                    />
                  </div>
                )}
                {schedule.unit === "once" && (
                  <Input
                    inputSize="lg"
                    type="date"
                    value={schedule.onceDate}
                    onChange={(e) =>
                      setSchedule((s) => ({ ...s, onceDate: e.target.value }))
                    }
                    className="w-[140px] [color-scheme:dark]"
                  />
                )}
                {(schedule.unit === "once" ||
                  schedule.unit === "daily" ||
                  schedule.unit === "weekly" ||
                  schedule.unit === "monthly") && (
                  <TimePicker
                    value={schedule.time}
                    onChange={(v) => setSchedule((s) => ({ ...s, time: v }))}
                  />
                )}
                {schedule.unit === "weekly" && (
                  <div className="w-[130px]">
                    <CustomSelect
                      value={String(schedule.dayOfWeek)}
                      onChange={(v) =>
                        setSchedule((s) => ({ ...s, dayOfWeek: Number(v) }))
                      }
                      options={DOW_OPTIONS}
                      size="md"
                    />
                  </div>
                )}
                {schedule.unit === "monthly" && (
                  <div className="w-[80px]">
                    <CustomSelect
                      value={String(schedule.dayOfMonth)}
                      onChange={(v) =>
                        setSchedule((s) => ({ ...s, dayOfMonth: Number(v) }))
                      }
                      options={DOM_OPTIONS}
                      size="md"
                    />
                  </div>
                )}
              </div>
            </div>
          )}

          {/* Action Type */}
          <div>
            <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
              {t("form.action")}
            </label>
            <CustomSelect
              value={actionType}
              onChange={(v) => setActionType(v as TaskActionType)}
              options={ACTION_OPTIONS}
              size="md"
            />
          </div>

          {/* Agent selector (conditional, optional) */}
          {actionType === "run-agent" && (
            <div>
              <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
                {t("form.agent")}{" "}
                <span className="text-ink-tertiary font-normal text-[12px]">
                  ({t("form.optional")})
                </span>
              </label>
              {agents.length > 0 ? (
                <CustomSelect
                  value={agentName}
                  onChange={setAgentName}
                  options={agents}
                  placeholder={t("input.selectAgent")}
                  size="md"
                />
              ) : (
                <div className="w-full rounded-lg bg-surface-2 border border-hairline px-3 py-4 flex flex-col items-center gap-2">
                  <div className="text-ink-tertiary text-[13px]">
                    {t("schedule.noAgents")}
                  </div>
                  <div className="text-ink-tertiary text-[11px] text-center leading-relaxed">
                    {t("schedule.noAgentsHint")}
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Skill selector (conditional) */}
          {actionType === "run-skill" && (
            <div>
              <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
                {t("form.skill")}
              </label>
              {skills.length > 0 ? (
                <CustomSelect
                  value={skillName}
                  onChange={setSkillName}
                  options={skills}
                  placeholder={t("input.selectSkill")}
                  size="md"
                />
              ) : (
                <div className="w-full rounded-lg bg-surface-2 border border-hairline px-3 py-4 flex flex-col items-center gap-2">
                  <div className="text-ink-tertiary text-[13px]">
                    {t("schedule.noSkills")}
                  </div>
                  <div className="text-ink-tertiary text-[11px] text-center leading-relaxed">
                    {t("schedule.noSkillsHint")}
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Notification Channel */}
          <div>
            <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
              {t("form.notifyChannel")}
            </label>
            <CustomSelect
              value={notifyChannel}
              onChange={setNotifyChannel}
              options={[
                { value: "", label: t("schedule.notifyNone") },
                ...channelOptions,
              ]}
              size="md"
            />
          </div>

          {/* Description / Prompt */}
          <div>
            <label className="block text-[13px] text-ink-muted mb-1.5 font-medium">
              {actionType === "custom-prompt"
                ? t("form.promptRequired")
                : t("form.description")}
            </label>
            <Textarea
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              placeholder={
                actionType === "custom-prompt"
                  ? t("form.promptPlaceholder")
                  : t("form.descriptionPlaceholder")
              }
              rows={actionType === "custom-prompt" ? 4 : 3}
              className="leading-relaxed"
            />
          </div>

          {/* Webhook URL — only in webhook mode */}
          {!isScheduleForm && (
            <div className="pt-2 border-t border-hairline">
              {task?.id ? (
                <div className="p-3 rounded-lg bg-surface-2 border border-hairline space-y-2">
                  <div className="text-[11px] text-ink-tertiary">
                    {t("schedule.webhookUrl")}
                  </div>
                  <div className="flex items-center gap-2">
                    <code className="flex-1 text-[11px] font-mono text-ink bg-surface-1 px-2 py-1.5 rounded truncate">
                      {typeof window !== "undefined"
                        ? `${window.location.origin}/api/webhooks/${task.id}`
                        : `/api/webhooks/${task.id}`}
                    </code>
                    <button
                      type="button"
                      onClick={() => {
                        const url = `${window.location.origin}/api/webhooks/${task.id}`;
                        navigator.clipboard.writeText(url);
                      }}
                      className="text-[11px] text-brand-primary hover:underline shrink-0"
                    >
                      {t("button.copy")}
                    </button>
                  </div>
                  <div className="text-[11px] text-ink-tertiary">
                    {t("schedule.webhookTokenHint")}{" "}
                    <code className="text-[10px] bg-surface-1 px-1 py-0.5 rounded">
                      X-Webhook-Token: {"<token>"}
                    </code>
                  </div>
                </div>
              ) : (
                <div className="text-[11px] text-ink-tertiary">
                  {t("schedule.webhookUrlAfterSave")}
                </div>
              )}
            </div>
          )}
        </div>

        {/* Footer */}
        <div className="flex justify-end gap-3 px-6 py-4 border-t border-hairline">
          <Button variant="secondary" size="md" onClick={onClose}>
            {t("common.cancel")}
          </Button>
          <Button
            variant="primary"
            size="md"
            onClick={handleSubmit}
            disabled={
              !name.trim() ||
              (actionType === "custom-prompt" && !description.trim()) ||
              submitting
            }
            loading={submitting}
          >
            {submitting
              ? "..."
              : mode === "edit"
                ? t("common.save")
                : t("button.createTask")}
          </Button>
        </div>
      </Card>
    </div>
  );
}
