"use client";

import { useQuery } from "@tanstack/react-query";
import {
  ArrowDown,
  Bot,
  Clock,
  Gavel,
  GitBranch,
  type LucideIcon,
  Terminal,
  Trash2,
} from "lucide-react";
import { useEffect, useRef, useState } from "react";
import { CONNECTOR_ORDER, connectorTitle, toolConnectorKey } from "@/components/connector-icon";
import { Field, Input, Select } from "@/components/ui";
import { api } from "@/lib/api";
import type { Tool } from "@/lib/types";

// -- Types -------------------------------------------------------------------

interface WorkflowNode {
  id: string;
  type: string;
  config: Record<string, unknown>;
}
interface WorkflowEdge {
  from: string;
  to: string;
  when?: string;
}
export interface CompiledWorkflow {
  definition: { nodes: WorkflowNode[]; edges: WorkflowEdge[] };
  triggerConfig: Record<string, unknown>;
  valid: boolean;
  stepCount: number;
}

type StepKind = "agent" | "tool" | "condition" | "approval" | "delay";

interface Step {
  uid: string;
  kind: StepKind;
  agentId?: string;
  input?: string;
  tool?: string;
  args?: Record<string, string>;
  field?: string;
  op?: string;
  value?: string;
  summary?: string;
  seconds?: string;
}

interface JsonSchema {
  properties?: Record<string, { type?: string; description?: string }>;
  required?: string[];
}

const STEP_META: Record<
  StepKind,
  { label: string; icon: LucideIcon; color: string; hint: string }
> = {
  agent: {
    label: "Run an agent",
    icon: Bot,
    color: "#c084fc",
    hint: "Let one of your agents reason and act.",
  },
  tool: {
    label: "Call a tool",
    icon: Terminal,
    color: "#38bdf8",
    hint: "Invoke a single connector action.",
  },
  condition: {
    label: "Check a condition",
    icon: GitBranch,
    color: "#fbbf24",
    hint: "Only keep going if this is true.",
  },
  approval: {
    label: "Require approval",
    icon: Gavel,
    color: "#fb7185",
    hint: "Pause until a human approves.",
  },
  delay: {
    label: "Wait",
    icon: Clock,
    color: "#2dd4bf",
    hint: "Pause for a set number of seconds.",
  },
};

const STEP_ORDER: StepKind[] = ["agent", "tool", "condition", "approval", "delay"];

const OPERATORS = [
  { value: "eq", label: "equals", needsValue: true },
  { value: "neq", label: "does not equal", needsValue: true },
  { value: "contains", label: "contains", needsValue: true },
  { value: "gt", label: "greater than", needsValue: true },
  { value: "lt", label: "less than", needsValue: true },
  { value: "is_true", label: "is true", needsValue: false },
  { value: "is_false", label: "is false", needsValue: false },
];

// -- Compilation -------------------------------------------------------------

const isNumeric = (v: string) => /^-?\d+(\.\d+)?$/.test(v.trim());

function literal(raw: string): string {
  const v = raw.trim();
  if (isNumeric(v) || v === "true" || v === "false") return v;
  return `'${v.replace(/'/g, "")}'`;
}

function compileCondition(step: Step): string {
  const ref = `{{ ${(step.field ?? "").trim()} }}`;
  const raw = (step.value ?? "").trim();
  switch (step.op) {
    case "neq":
      return `${ref} != ${literal(raw)}`;
    case "contains":
      return `'${raw.replace(/'/g, "")}' in ${ref}`;
    case "gt":
      return `${ref} > ${isNumeric(raw) ? raw : literal(raw)}`;
    case "lt":
      return `${ref} < ${isNumeric(raw) ? raw : literal(raw)}`;
    case "is_true":
      return `${ref} == true`;
    case "is_false":
      return `${ref} == false`;
    default:
      return `${ref} == ${literal(raw)}`;
  }
}

function cleanArgs(args: Record<string, string> | undefined): Record<string, string> {
  const out: Record<string, string> = {};
  for (const [k, v] of Object.entries(args ?? {})) if (v !== "") out[k] = v;
  return out;
}

function stepValid(step: Step): boolean {
  switch (step.kind) {
    case "agent":
      return !!step.agentId;
    case "tool":
      return !!step.tool;
    case "condition":
      return !!(step.field ?? "").trim();
    default:
      return true;
  }
}

function compileNode(step: Step): WorkflowNode {
  const id = step.uid;
  switch (step.kind) {
    case "agent":
      return {
        id,
        type: "agent",
        config: { agent_id: step.agentId ?? "", input: step.input ?? "" },
      };
    case "tool":
      return { id, type: "tool", config: { tool: step.tool ?? "", args: cleanArgs(step.args) } };
    case "condition":
      return { id, type: "condition", config: { expr: compileCondition(step) } };
    case "approval":
      return { id, type: "approval", config: { summary: step.summary ?? "" } };
    case "delay":
      return { id, type: "delay", config: { seconds: Number(step.seconds) || 0 } };
  }
}

function compile(
  steps: Step[],
  trigger: string,
  cron: string,
  eventName: string
): CompiledWorkflow {
  const nodes: WorkflowNode[] = [{ id: "trigger", type: "trigger", config: {} }];
  const edges: WorkflowEdge[] = [];
  let prevId = "trigger";
  let prevKind = "trigger";
  for (const step of steps) {
    nodes.push(compileNode(step));
    const edge: WorkflowEdge = { from: prevId, to: step.uid };
    if (prevKind === "condition") edge.when = "true";
    edges.push(edge);
    prevId = step.uid;
    prevKind = step.kind;
  }

  const triggerConfig: Record<string, unknown> = {};
  if (trigger === "schedule" && cron.trim()) triggerConfig.cron = cron.trim();
  if (trigger === "event" && eventName.trim()) triggerConfig.event = eventName.trim();

  const valid =
    steps.length > 0 &&
    steps.every(stepValid) &&
    (trigger !== "schedule" || cron.trim().length > 0);

  return { definition: { nodes, edges }, triggerConfig, valid, stepCount: steps.length };
}

// -- Tool argument fields ----------------------------------------------------

function ToolArgs({
  tool,
  args,
  onChange,
}: {
  tool: Tool | undefined;
  args: Record<string, string>;
  onChange: (key: string, value: string) => void;
}) {
  const schema = (tool?.parameters ?? {}) as JsonSchema;
  const props = schema.properties ?? {};
  const required = new Set(schema.required ?? []);
  const keys = Object.keys(props);
  if (keys.length === 0) return null;
  return (
    <div className="mt-3 space-y-3 rounded-lg border border-line bg-ink/30 p-3">
      <span className="eyebrow">Arguments</span>
      {keys.map((key) => (
        <Field
          key={key}
          label={`${key}${required.has(key) ? " *" : ""}`}
          hint={props[key].description}
        >
          <Input
            value={args[key] ?? ""}
            onChange={(e) => onChange(key, e.target.value)}
            placeholder={
              props[key].type === "object" ? '{"key":"value"}' : "value or {{ trigger.field }}"
            }
          />
        </Field>
      ))}
    </div>
  );
}

// -- Builder -----------------------------------------------------------------

let uidCounter = 0;
const nextUid = () => `s${++uidCounter}`;

export function WorkflowBuilder({
  trigger,
  onChange,
}: {
  trigger: string;
  onChange: (c: CompiledWorkflow) => void;
}) {
  const agents = useQuery({ queryKey: ["agents"], queryFn: api.listAgents });
  const tools = useQuery({ queryKey: ["tools"], queryFn: api.listTools });
  const [steps, setSteps] = useState<Step[]>([]);
  const [cron, setCron] = useState("");
  const [eventName, setEventName] = useState("");

  const onChangeRef = useRef(onChange);
  onChangeRef.current = onChange;
  useEffect(() => {
    onChangeRef.current(compile(steps, trigger, cron, eventName));
  }, [steps, trigger, cron, eventName]);

  const patch = (uid: string, changes: Partial<Step>) =>
    setSteps((cur) => cur.map((s) => (s.uid === uid ? { ...s, ...changes } : s)));
  const remove = (uid: string) => setSteps((cur) => cur.filter((s) => s.uid !== uid));
  const move = (uid: string, dir: -1 | 1) =>
    setSteps((cur) => {
      const i = cur.findIndex((s) => s.uid === uid);
      const j = i + dir;
      if (i < 0 || j < 0 || j >= cur.length) return cur;
      const next = [...cur];
      [next[i], next[j]] = [next[j], next[i]];
      return next;
    });
  const add = (kind: StepKind) =>
    setSteps((cur) => [
      ...cur,
      { uid: nextUid(), kind, op: kind === "condition" ? "eq" : undefined, args: {} },
    ]);

  const toolByName = (name?: string) => (tools.data ?? []).find((t) => t.name === name);

  // Tools grouped for the picker.
  const toolGroups = groupTools(tools.data ?? []);

  return (
    <div className="space-y-2">
      {/* Trigger (fixed entry point) */}
      <StepCard color="#f5a524" icon="trigger" title="Trigger" subtitle={triggerSubtitle(trigger)}>
        {trigger === "schedule" && (
          <Field label="Cron schedule" hint="Standard cron, e.g. 0 9 * * 1-5 (weekdays at 9am).">
            <Input
              value={cron}
              onChange={(e) => setCron(e.target.value)}
              placeholder="0 9 * * 1-5"
            />
          </Field>
        )}
        {trigger === "event" && (
          <Field label="Event name" hint="Runs when another workflow emits this event.">
            <Input
              value={eventName}
              onChange={(e) => setEventName(e.target.value)}
              placeholder="lead.created"
            />
          </Field>
        )}
      </StepCard>

      {steps.map((step, i) => {
        const meta = STEP_META[step.kind];
        return (
          <div key={step.uid}>
            <Connector />
            <StepCard
              color={meta.color}
              icon={step.kind}
              title={`${i + 1}. ${meta.label}`}
              onUp={i > 0 ? () => move(step.uid, -1) : undefined}
              onDown={i < steps.length - 1 ? () => move(step.uid, 1) : undefined}
              onRemove={() => remove(step.uid)}
            >
              {step.kind === "agent" && (
                <div className="space-y-3">
                  <Field label="Agent">
                    <Select
                      value={step.agentId ?? ""}
                      onChange={(e) => patch(step.uid, { agentId: e.target.value })}
                    >
                      <option value="">Select an agent…</option>
                      {(agents.data ?? []).map((a) => (
                        <option key={a.id} value={a.id}>
                          {a.name}
                        </option>
                      ))}
                    </Select>
                  </Field>
                  {(agents.data ?? []).length === 0 && (
                    <p className="text-xs text-warning">
                      No agents yet. Create one under Agents first.
                    </p>
                  )}
                  <Field
                    label="Input"
                    hint="Plain text, or reference the trigger, e.g. {{ trigger.query }}."
                  >
                    <Input
                      value={step.input ?? ""}
                      onChange={(e) => patch(step.uid, { input: e.target.value })}
                      placeholder="{{ trigger.message }}"
                    />
                  </Field>
                </div>
              )}

              {step.kind === "tool" && (
                <div>
                  <Field label="Tool">
                    <Select
                      value={step.tool ?? ""}
                      onChange={(e) => patch(step.uid, { tool: e.target.value, args: {} })}
                    >
                      <option value="">Select a tool…</option>
                      {toolGroups.map((g) => (
                        <optgroup key={g.key} label={connectorTitle(g.key)}>
                          {g.tools.map((t) => (
                            <option key={t.name} value={t.name}>
                              {t.name}
                              {t.is_sensitive ? "  (needs approval)" : ""}
                            </option>
                          ))}
                        </optgroup>
                      ))}
                    </Select>
                  </Field>
                  <ToolArgs
                    tool={toolByName(step.tool)}
                    args={step.args ?? {}}
                    onChange={(k, v) => patch(step.uid, { args: { ...(step.args ?? {}), [k]: v } })}
                  />
                </div>
              )}

              {step.kind === "condition" && (
                <div className="space-y-3">
                  <div className="grid gap-2 sm:grid-cols-[1fr_auto]">
                    <Field
                      label="Field"
                      hint="A value from an earlier step, e.g. trigger.priority."
                    >
                      <Input
                        value={step.field ?? ""}
                        onChange={(e) => patch(step.uid, { field: e.target.value })}
                        placeholder="trigger.priority"
                      />
                    </Field>
                    <Field label="Is">
                      <Select
                        value={step.op ?? "eq"}
                        onChange={(e) => patch(step.uid, { op: e.target.value })}
                      >
                        {OPERATORS.map((o) => (
                          <option key={o.value} value={o.value}>
                            {o.label}
                          </option>
                        ))}
                      </Select>
                    </Field>
                  </div>
                  {OPERATORS.find((o) => o.value === (step.op ?? "eq"))?.needsValue && (
                    <Field label="Value">
                      <Input
                        value={step.value ?? ""}
                        onChange={(e) => patch(step.uid, { value: e.target.value })}
                        placeholder="high"
                      />
                    </Field>
                  )}
                  <p className="text-xs text-faint">If this is false, the workflow stops here.</p>
                </div>
              )}

              {step.kind === "approval" && (
                <Field label="Summary" hint="Shown to the approver in their inbox.">
                  <Input
                    value={step.summary ?? ""}
                    onChange={(e) => patch(step.uid, { summary: e.target.value })}
                    placeholder="Approve before sending the email"
                  />
                </Field>
              )}

              {step.kind === "delay" && (
                <Field label="Seconds" hint="How long to wait before the next step.">
                  <Input
                    type="number"
                    value={step.seconds ?? ""}
                    onChange={(e) => patch(step.uid, { seconds: e.target.value })}
                    placeholder="60"
                  />
                </Field>
              )}
            </StepCard>
          </div>
        );
      })}

      {/* Add-step palette */}
      <Connector />
      <div className="rounded-xl border border-dashed border-line p-3">
        <div className="mb-2 eyebrow">Add a step</div>
        <div className="flex flex-wrap gap-2">
          {STEP_ORDER.map((kind) => {
            const meta = STEP_META[kind];
            const Icon = meta.icon;
            return (
              <button
                key={kind}
                type="button"
                onClick={() => add(kind)}
                title={meta.hint}
                className="flex items-center gap-2 rounded-lg border border-line px-3 py-2 text-sm text-muted transition-colors hover:border-line-strong hover:text-fg"
              >
                <Icon className="h-4 w-4" style={{ color: meta.color }} />
                {meta.label}
              </button>
            );
          })}
        </div>
      </div>

      {steps.length === 0 && (
        <p className="pt-1 text-center text-xs text-faint">
          Add your first step above to build the workflow.
        </p>
      )}
    </div>
  );
}

// -- Presentational helpers --------------------------------------------------

function triggerSubtitle(trigger: string): string {
  switch (trigger) {
    case "webhook":
      return "Starts when your signed webhook URL is called.";
    case "schedule":
      return "Starts on a schedule.";
    case "event":
      return "Starts when a matching event is emitted.";
    default:
      return "Starts when you run it from here or the API.";
  }
}

const STEP_ICON: Record<string, LucideIcon> = {
  agent: Bot,
  tool: Terminal,
  condition: GitBranch,
  approval: Gavel,
  delay: Clock,
};

function StepCard({
  color,
  icon,
  title,
  subtitle,
  children,
  onUp,
  onDown,
  onRemove,
}: {
  color: string;
  icon: string;
  title: string;
  subtitle?: string;
  children?: React.ReactNode;
  onUp?: () => void;
  onDown?: () => void;
  onRemove?: () => void;
}) {
  const Icon = STEP_ICON[icon];
  return (
    <div className="rounded-xl border border-line bg-surface/50 p-4">
      <div className="flex items-center gap-3">
        <span
          className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border"
          style={{ borderColor: `${color}55`, backgroundColor: `${color}1a`, color }}
        >
          {icon === "trigger" ? (
            <svg
              viewBox="0 0 24 24"
              className="h-[18px] w-[18px]"
              fill="none"
              stroke="currentColor"
              strokeWidth="2"
              strokeLinecap="round"
              strokeLinejoin="round"
            >
              <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
            </svg>
          ) : (
            Icon && <Icon className="h-[18px] w-[18px]" />
          )}
        </span>
        <div className="min-w-0 flex-1">
          <div className="font-display text-sm font-semibold text-fg">{title}</div>
          {subtitle && <div className="mt-0.5 text-xs text-muted">{subtitle}</div>}
        </div>
        <div className="flex items-center gap-1">
          {onUp && (
            <IconBtn onClick={onUp} label="Move up">
              ↑
            </IconBtn>
          )}
          {onDown && (
            <IconBtn onClick={onDown} label="Move down">
              ↓
            </IconBtn>
          )}
          {onRemove && (
            <button
              type="button"
              onClick={onRemove}
              className="rounded-md p-1.5 text-faint transition-colors hover:bg-surface-2 hover:text-danger"
              title="Remove step"
            >
              <Trash2 className="h-4 w-4" />
            </button>
          )}
        </div>
      </div>
      {children && <div className="mt-4">{children}</div>}
    </div>
  );
}

function IconBtn({
  children,
  onClick,
  label,
}: {
  children: React.ReactNode;
  onClick: () => void;
  label: string;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      title={label}
      className="rounded-md px-1.5 py-1 font-mono text-xs text-faint transition-colors hover:bg-surface-2 hover:text-fg"
    >
      {children}
    </button>
  );
}

function Connector() {
  return (
    <div className="flex justify-center py-1 text-faint">
      <ArrowDown className="h-4 w-4" />
    </div>
  );
}

function groupTools(all: Tool[]): { key: string; tools: Tool[] }[] {
  const byKey: Record<string, Tool[]> = {};
  for (const t of all) (byKey[toolConnectorKey(t.name)] ||= []).push(t);
  return Object.keys(byKey)
    .sort((a, b) => {
      const ia = CONNECTOR_ORDER.indexOf(a);
      const ib = CONNECTOR_ORDER.indexOf(b);
      return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib);
    })
    .map((key) => ({ key, tools: byKey[key] }));
}
