import { Clock, Flag, GitBranch, Headset, Image as ImageIcon, ListChecks, MessageSquareText, MousePointerClick, Play, UserCog } from "lucide-react";
import type { ReactNode } from "react";
import type { FlowNodeType, InteractiveContent } from "@/lib/types";

export interface NodeMeta {
  label: string;
  hint: string;
  icon: ReactNode;
  /** Accent for the node header (Tailwind classes). */
  accent: string;
  waits?: boolean;
}

export const NODE_META: Record<FlowNodeType, NodeMeta> = {
  start: { label: "Start", hint: "Where the flow begins", icon: <Play />, accent: "bg-ink text-white" },
  text: { label: "Text message", hint: "Send a message", icon: <MessageSquareText />, accent: "bg-teal-soft text-teal" },
  media: { label: "Media message", hint: "Send an image, video, audio or document", icon: <ImageIcon />, accent: "bg-teal-soft text-teal" },
  interactive: { label: "Interactive message", hint: "Buttons, a list or a link; routes on the reply", icon: <MousePointerClick />, accent: "bg-teal-soft text-teal" },
  choice: { label: "Ask a question", hint: "Wait for a typed answer and route on it", icon: <ListChecks />, accent: "bg-[#e8eefc] text-[#2f4fa8]" },
  condition: { label: "Condition", hint: "Branch on contact details", icon: <GitBranch />, accent: "bg-[#e8eefc] text-[#2f4fa8]" },
  delay: { label: "Delay", hint: "Wait before the next step", icon: <Clock />, accent: "bg-amber-soft text-amber" },
  contact_update: { label: "Update contact", hint: "Set fields, tags, groups or consent", icon: <UserCog />, accent: "bg-amber-soft text-amber" },
  handoff: { label: "Hand off to a person", hint: "Assign the chat and pause automation", icon: <Headset />, accent: "bg-brick-soft text-brick" },
  end: { label: "End", hint: "Finish the flow", icon: <Flag />, accent: "bg-paper text-ink" },
};

/** Order for the "Add step" menu, grouped like the reference's "Add New Bot Reply" dropdown. */
export const ADDABLE: { heading: string; types: FlowNodeType[] }[] = [
  { heading: "Messages", types: ["text", "media", "interactive"] },
  { heading: "Logic", types: ["choice", "condition", "delay"] },
  { heading: "Actions", types: ["contact_update", "handoff", "end"] },
];

export function defaultData(type: FlowNodeType): Record<string, unknown> {
  switch (type) {
    case "text":
      return { body: "" };
    case "media":
      return { media_type: "image", asset_id: null, caption: "" };
    case "interactive":
      return { header: { type: "none" }, body: "", footer: "", action: { type: "buttons", buttons: [{ title: "" }, { title: "" }] } } satisfies InteractiveContent;
    case "choice":
      return { prompt: "", options: [{ label: "", match: "contains_word", value: "" }] };
    case "condition":
      return { match: "all", rules: [{ field: "country", operator: "equals", value: "" }] };
    case "delay":
      return { amount: 5, unit: "seconds" };
    case "contact_update":
      return { actions: [{ type: "set_opt_in", value: "opted_in" }] };
    case "handoff":
      return { message: "Connecting you to our team. Someone will reply here shortly.", assign_to: null, note: "" };
    case "end":
      return { message: "" };
    default:
      return {};
  }
}

const rid = () => Math.random().toString(16).slice(2, 12);
export const newNodeId = (type: string) => `${type.slice(0, 4)}_${rid()}`;

/** Gives new buttons, rows and options ids on the client so they can be connected before the first save. */
export function ensureIds(type: FlowNodeType, data: Record<string, unknown>): Record<string, unknown> {
  const used = new Set<string>();
  const take = (id: unknown, prefix: string) => {
    let v = typeof id === "string" && /^[A-Za-z0-9_.:-]{1,64}$/.test(id) && !used.has(id) ? id : `${prefix}_${rid()}`;
    while (used.has(v)) v = `${prefix}_${rid()}`;
    used.add(v);
    return v;
  };
  if (type === "interactive") {
    const a = (data as unknown as InteractiveContent).action;
    if (a?.type === "buttons") return { ...data, action: { ...a, buttons: a.buttons.map((b) => ({ ...b, id: take(b.id, "btn") })) } };
    if (a?.type === "list") return { ...data, action: { ...a, sections: a.sections.map((s) => ({ ...s, rows: s.rows.map((r) => ({ ...r, id: take(r.id, "opt") })) })) } };
  }
  if (type === "choice" && Array.isArray(data.options)) return { ...data, options: (data.options as { id?: string }[]).map((o) => ({ ...o, id: take(o.id, "opt") })) };
  return data;
}

/** Output handles in display order. Must mirror the server's outputsOf(). */
export function outputsOf(type: FlowNodeType, data: Record<string, unknown>): { id: string; label: string; muted?: boolean }[] {
  switch (type) {
    case "start":
    case "text":
    case "media":
    case "delay":
    case "contact_update":
      return [{ id: "next", label: "" }];
    case "interactive": {
      const a = (data as unknown as InteractiveContent).action;
      if (a?.type === "buttons") return [...a.buttons.map((b, i) => ({ id: String(b.id ?? ""), label: b.title || `Button ${i + 1}` })), { id: "no_match", label: "Typed something else", muted: true }];
      if (a?.type === "list") return [...a.sections.flatMap((s) => s.rows).map((r, i) => ({ id: String(r.id ?? ""), label: r.title || `Option ${i + 1}` })), { id: "no_match", label: "Typed something else", muted: true }];
      return [{ id: "next", label: "" }];
    }
    case "choice":
      return [...((data.options as { id?: string; label?: string }[]) ?? []).map((o, i) => ({ id: String(o.id ?? ""), label: o.label || `Option ${i + 1}` })), { id: "no_match", label: "No option matched", muted: true }];
    case "condition":
      return [
        { id: "true", label: "Yes" },
        { id: "false", label: "No" },
      ];
    default:
      return [];
  }
}

export const waitsForReply = (type: FlowNodeType, data: Record<string, unknown>) => type === "choice" || (type === "interactive" && ["buttons", "list"].includes((data as unknown as InteractiveContent).action?.type));

const clip = (s: unknown, n = 90) => {
  const t = String(s ?? "").replace(/\s+/g, " ").trim();
  return t.length > n ? `${t.slice(0, n - 1)}…` : t;
};

/** One or two lines describing a node on the canvas. */
export function summaryOf(type: FlowNodeType, data: Record<string, unknown>, trigger?: string | null): string {
  switch (type) {
    case "start":
      return trigger ? `When someone sends “${clip(trigger, 60)}”` : "When the trigger matches";
    case "text":
      return clip(data.body) || "Empty message";
    case "media":
      return `${String(data.media_type ?? "media")[0].toUpperCase()}${String(data.media_type ?? "media").slice(1)}${data.caption ? `: ${clip(data.caption, 70)}` : ""}${data.asset_id ? "" : " (no file yet)"}`;
    case "interactive":
      return clip(data.body) || "Empty message";
    case "choice":
      return clip(data.prompt) || "Waits for a typed answer";
    case "condition": {
      const rules = (data.rules as { field: string; operator: string; value: string }[]) ?? [];
      return rules.map((r) => `${r.field.replace("custom:", "")} ${r.operator.replace(/_/g, " ")} ${r.value ?? ""}`.trim()).join(data.match === "any" ? " or " : " and ") || "No rules";
    }
    case "delay":
      return `Wait ${data.amount ?? "?"} ${data.unit ?? ""}`;
    case "contact_update": {
      const n = ((data.actions as unknown[]) ?? []).length;
      return `${n} change${n === 1 ? "" : "s"}`;
    }
    case "handoff":
      return clip(data.message) || "Assign to the team";
    case "end":
      return clip(data.message) || "Flow ends";
  }
}
