import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import {
  createAgendaCard,
  createAppleTvRemoteCard,
  createDeviceControlCard,
  createEventCard,
  createMediaPlayerCard,
} from "./flex-templates.js";
import type { LineChannelData } from "./types.js";

/**
 * Parse LINE-specific directives from text and extract them into ReplyPayload fields.
 *
 * Supported directives:
 * - [[quick_replies: option1, option2, option3]]
 * - [[location: title | address | latitude | longitude]]
 * - [[confirm: question | yes_label | no_label]]
 * - [[buttons: title | text | btn1:data1, btn2:data2]]
 * - [[media_player: title | artist | source | imageUrl | playing/paused]]
 * - [[event: title | date | time | location | description]]
 * - [[agenda: title | event1_title:event1_time, event2_title:event2_time, ...]]
 * - [[device: name | type | status | ctrl1:data1, ctrl2:data2]]
 * - [[appletv_remote: name | status]]
 */
export function parseLineDirectives(payload: ReplyPayload): ReplyPayload {
  let text = payload.text;
  if (!text) {
    return payload;
  }

  const result: ReplyPayload = { ...payload };
  const lineData: LineChannelData = {
    ...(result.channelData?.line as LineChannelData | undefined),
  };
  const toSlug = (value: string): string =>
    normalizeLowercaseStringOrEmpty(value)
      .replace(/[^a-z0-9]+/g, "_")
      .replace(/^_+|_+$/g, "") || "device";
  const lineActionData = (action: string, extras?: Record<string, string>): string => {
    const base = [`line.action=${encodeURIComponent(action)}`];
    if (extras) {
      for (const [key, value] of Object.entries(extras)) {
        base.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
      }
    }
    return base.join("&");
  };

  const quickRepliesMatch = text.match(/\[\[quick_replies:\s*([^\]]+)\]\]/i);
  if (quickRepliesMatch) {
    const options = quickRepliesMatch[1]
      .split(",")
      .map((s) => s.trim())
      .filter(Boolean);
    if (options.length > 0) {
      lineData.quickReplies = [...(lineData.quickReplies || []), ...options];
    }
    text = text.replace(quickRepliesMatch[0], "").trim();
  }

  const locationMatch = text.match(/\[\[location:\s*([^\]]+)\]\]/i);
  if (locationMatch && !lineData.location) {
    const parts = locationMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 4) {
      const [title, address, latStr, lonStr] = parts;
      const latitude = parseFloat(latStr);
      const longitude = parseFloat(lonStr);
      if (!isNaN(latitude) && !isNaN(longitude)) {
        lineData.location = {
          title: title || "Location",
          address: address || "",
          latitude,
          longitude,
        };
      }
    }
    text = text.replace(locationMatch[0], "").trim();
  }

  const confirmMatch = text.match(/\[\[confirm:\s*([^\]]+)\]\]/i);
  if (confirmMatch && !lineData.templateMessage) {
    const parts = confirmMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 3) {
      const [question, yesPart, noPart] = parts;
      const [yesLabel, yesData] = yesPart.includes(":")
        ? yesPart.split(":").map((s) => s.trim())
        : [yesPart, normalizeLowercaseStringOrEmpty(yesPart)];
      const [noLabel, noData] = noPart.includes(":")
        ? noPart.split(":").map((s) => s.trim())
        : [noPart, normalizeLowercaseStringOrEmpty(noPart)];

      lineData.templateMessage = {
        type: "confirm",
        text: question,
        confirmLabel: yesLabel,
        confirmData: yesData,
        cancelLabel: noLabel,
        cancelData: noData,
        altText: question,
      };
    }
    text = text.replace(confirmMatch[0], "").trim();
  }

  const buttonsMatch = text.match(/\[\[buttons:\s*([^\]]+)\]\]/i);
  if (buttonsMatch && !lineData.templateMessage) {
    const parts = buttonsMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 3) {
      const [title, bodyText, actionsStr] = parts;

      const actions = actionsStr.split(",").map((actionStr) => {
        const trimmed = actionStr.trim();
        const colonIndex = (() => {
          const index = trimmed.indexOf(":");
          if (index === -1) {
            return -1;
          }
          const lower = normalizeLowercaseStringOrEmpty(trimmed);
          if (lower.startsWith("http://") || lower.startsWith("https://")) {
            return -1;
          }
          return index;
        })();

        let label: string;
        let data: string;

        if (colonIndex === -1) {
          label = trimmed;
          data = trimmed;
        } else {
          label = trimmed.slice(0, colonIndex).trim();
          data = trimmed.slice(colonIndex + 1).trim();
        }

        if (data.startsWith("http://") || data.startsWith("https://")) {
          return { type: "uri" as const, label, uri: data };
        }
        if (data.includes("=")) {
          return { type: "postback" as const, label, data };
        }
        return { type: "message" as const, label, data: data || label };
      });

      if (actions.length > 0) {
        lineData.templateMessage = {
          type: "buttons",
          title,
          text: bodyText,
          actions: actions.slice(0, 4),
          altText: `${title}: ${bodyText}`,
        };
      }
    }
    text = text.replace(buttonsMatch[0], "").trim();
  }

  const mediaPlayerMatch = text.match(/\[\[media_player:\s*([^\]]+)\]\]/i);
  if (mediaPlayerMatch && !lineData.flexMessage) {
    const parts = mediaPlayerMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [title, artist, source, imageUrl, statusStr] = parts;
      const isPlaying = normalizeLowercaseStringOrEmpty(statusStr) === "playing";
      const validImageUrl = imageUrl?.startsWith("https://") ? imageUrl : undefined;
      const deviceKey = toSlug(source || title || "media");
      const card = createMediaPlayerCard({
        title: title || "Unknown Track",
        subtitle: artist || undefined,
        source: source || undefined,
        imageUrl: validImageUrl,
        isPlaying: statusStr ? isPlaying : undefined,
        controls: {
          previous: { data: lineActionData("previous", { "line.device": deviceKey }) },
          play: { data: lineActionData("play", { "line.device": deviceKey }) },
          pause: { data: lineActionData("pause", { "line.device": deviceKey }) },
          next: { data: lineActionData("next", { "line.device": deviceKey }) },
        },
      });

      lineData.flexMessage = {
        altText: `🎵 ${title}${artist ? ` - ${artist}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(mediaPlayerMatch[0], "").trim();
  }

  const eventMatch = text.match(/\[\[event:\s*([^\]]+)\]\]/i);
  if (eventMatch && !lineData.flexMessage) {
    const parts = eventMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 2) {
      const [title, date, time, location, description] = parts;

      const card = createEventCard({
        title: title || "Event",
        date: date || "TBD",
        time: time || undefined,
        location: location || undefined,
        description: description || undefined,
      });

      lineData.flexMessage = {
        altText: `📅 ${title} - ${date}${time ? ` ${time}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(eventMatch[0], "").trim();
  }

  const appleTvMatch = text.match(/\[\[appletv_remote:\s*([^\]]+)\]\]/i);
  if (appleTvMatch && !lineData.flexMessage) {
    const parts = appleTvMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [deviceName, status] = parts;
      const deviceKey = toSlug(deviceName || "apple_tv");

      const card = createAppleTvRemoteCard({
        deviceName: deviceName || "Apple TV",
        status: status || undefined,
        actionData: {
          up: lineActionData("up", { "line.device": deviceKey }),
          down: lineActionData("down", { "line.device": deviceKey }),
          left: lineActionData("left", { "line.device": deviceKey }),
          right: lineActionData("right", { "line.device": deviceKey }),
          select: lineActionData("select", { "line.device": deviceKey }),
          menu: lineActionData("menu", { "line.device": deviceKey }),
          home: lineActionData("home", { "line.device": deviceKey }),
          play: lineActionData("play", { "line.device": deviceKey }),
          pause: lineActionData("pause", { "line.device": deviceKey }),
          volumeUp: lineActionData("volume_up", { "line.device": deviceKey }),
          volumeDown: lineActionData("volume_down", { "line.device": deviceKey }),
          mute: lineActionData("mute", { "line.device": deviceKey }),
        },
      });

      lineData.flexMessage = {
        altText: `📺 ${deviceName || "Apple TV"} Remote`,
        contents: card,
      };
    }
    text = text.replace(appleTvMatch[0], "").trim();
  }

  const agendaMatch = text.match(/\[\[agenda:\s*([^\]]+)\]\]/i);
  if (agendaMatch && !lineData.flexMessage) {
    const parts = agendaMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 2) {
      const [title, eventsStr] = parts;
      const events = eventsStr.split(",").map((eventStr) => {
        const trimmed = eventStr.trim();
        const colonIdx = trimmed.lastIndexOf(":");
        if (colonIdx > 0) {
          return {
            title: trimmed.slice(0, colonIdx).trim(),
            time: trimmed.slice(colonIdx + 1).trim(),
          };
        }
        return { title: trimmed };
      });

      const card = createAgendaCard({
        title: title || "Agenda",
        events,
      });

      lineData.flexMessage = {
        altText: `📋 ${title} (${events.length} events)`,
        contents: card,
      };
    }
    text = text.replace(agendaMatch[0], "").trim();
  }

  const deviceMatch = text.match(/\[\[device:\s*([^\]]+)\]\]/i);
  if (deviceMatch && !lineData.flexMessage) {
    const parts = deviceMatch[1].split("|").map((s) => s.trim());
    if (parts.length >= 1) {
      const [deviceName, deviceType, status, controlsStr] = parts;
      const deviceKey = toSlug(deviceName || "device");
      const controls = controlsStr
        ? controlsStr.split(",").map((ctrlStr) => {
            const [label, data] = ctrlStr.split(":").map((s) => s.trim());
            const action = data || normalizeLowercaseStringOrEmpty(label).replace(/\s+/g, "_");
            return { label, data: lineActionData(action, { "line.device": deviceKey }) };
          })
        : [];

      const card = createDeviceControlCard({
        deviceName: deviceName || "Device",
        deviceType: deviceType || undefined,
        status: status || undefined,
        controls,
      });

      lineData.flexMessage = {
        altText: `📱 ${deviceName}${status ? `: ${status}` : ""}`,
        contents: card,
      };
    }
    text = text.replace(deviceMatch[0], "").trim();
  }

  text = text.replace(/\n{3,}/g, "\n\n").trim();

  result.text = text || undefined;
  if (Object.keys(lineData).length > 0) {
    result.channelData = { ...result.channelData, line: lineData };
  }
  return result;
}

export function hasLineDirectives(text: string): boolean {
  return /\[\[(quick_replies|location|confirm|buttons|media_player|event|agenda|device|appletv_remote):/i.test(
    text,
  );
}
