"use client";

import { Handle, Position, type Node, type NodeProps } from "@xyflow/react";
import { useTranslations } from "next-intl";

import BaseNodeWrapper from "./base-node-wrapper";

type AgentOutputNodeData = {
  isSkeleton?: boolean;
  stepId?: string;
  stepIndex?: number;
  stepTotal?: number;
  title?: string;
  channel?: string;
  artifactType?: string;
  previewText?: string;
  sections?: Array<{
    id?: string;
    label?: string;
    content?: string;
  }>;
  metadata?: Record<string, string | string[] | unknown>;
  qualityChecks?: string[];
  outputType?: string;
  body?: string;
  _status?: string;
  _statusMessage?: string;
};

type AgentOutputNodeType = Node<AgentOutputNodeData, "agent-output">;

function tryFormatJsonBody(body: string): string | null {
  const trimmed = body.trim();
  if (!trimmed) {
    return null;
  }

  const looksLikeJsonObject = trimmed.startsWith("{") && trimmed.endsWith("}");
  const looksLikeJsonArray = trimmed.startsWith("[") && trimmed.endsWith("]");
  if (!looksLikeJsonObject && !looksLikeJsonArray) {
    return null;
  }

  try {
    const parsed = JSON.parse(trimmed) as unknown;
    return JSON.stringify(parsed, null, 2);
  } catch {
    return null;
  }
}

function normalizeSections(raw: AgentOutputNodeData["sections"]) {
  if (!Array.isArray(raw)) {
    return [] as Array<{ id: string; label: string; content: string }>;
  }

  const sections: Array<{ id: string; label: string; content: string }> = [];
  for (const item of raw) {
    const label = typeof item?.label === "string" ? item.label.trim() : "";
    const content = typeof item?.content === "string" ? item.content.trim() : "";
    if (label === "" || content === "") {
      continue;
    }
    const id = typeof item.id === "string" && item.id.trim() !== "" ? item.id.trim() : label;
    sections.push({ id, label, content });
  }

  return sections;
}

function normalizeMetadata(raw: AgentOutputNodeData["metadata"]) {
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
    return [] as Array<[string, string]>;
  }

  const entries: Array<[string, string]> = [];
  for (const [rawKey, rawValue] of Object.entries(raw)) {
    const key = rawKey.trim();
    if (key === "") {
      continue;
    }

    if (typeof rawValue === "string") {
      const value = rawValue.trim();
      if (value !== "") {
        entries.push([key, value]);
      }
      continue;
    }

    if (Array.isArray(rawValue)) {
      const values = rawValue
        .filter((value): value is string => typeof value === "string")
        .map((value) => value.trim())
        .filter((value) => value !== "");
      if (values.length > 0) {
        entries.push([key, values.join(", ")]);
      }
    }
  }

  return entries;
}

function normalizeQualityChecks(raw: AgentOutputNodeData["qualityChecks"]): string[] {
  if (!Array.isArray(raw)) {
    return [];
  }

  return raw
    .filter((value): value is string => typeof value === "string")
    .map((value) => value.trim())
    .filter((value) => value !== "");
}

export default function AgentOutputNode({ data, selected }: NodeProps<AgentOutputNodeType>) {
  const t = useTranslations("agentOutputNode");
  const nodeData = data as AgentOutputNodeData;
  const isSkeleton = nodeData.isSkeleton === true;
  const hasStepCounter =
    typeof nodeData.stepIndex === "number" &&
    Number.isFinite(nodeData.stepIndex) &&
    typeof nodeData.stepTotal === "number" &&
    Number.isFinite(nodeData.stepTotal) &&
    nodeData.stepTotal > 0;
  const safeStepIndex =
    typeof nodeData.stepIndex === "number" && Number.isFinite(nodeData.stepIndex)
      ? Math.max(0, Math.floor(nodeData.stepIndex))
      : 0;
  const safeStepTotal =
    typeof nodeData.stepTotal === "number" && Number.isFinite(nodeData.stepTotal)
      ? Math.max(1, Math.floor(nodeData.stepTotal))
      : 1;
  const stepCounter = hasStepCounter
    ? `${safeStepIndex + 1}/${safeStepTotal}`
    : null;
  const resolvedTitle =
    nodeData.title ??
    (isSkeleton ? t("plannedOutputDefaultTitle") : t("defaultTitle"));
  const body = nodeData.body ?? "";
  const artifactType = nodeData.artifactType ?? nodeData.outputType ?? "";
  const sections = normalizeSections(nodeData.sections);
  const metadataEntries = normalizeMetadata(nodeData.metadata);
  const qualityChecks = normalizeQualityChecks(nodeData.qualityChecks);
  const previewText =
    typeof nodeData.previewText === "string" && nodeData.previewText.trim() !== ""
      ? nodeData.previewText.trim()
      : sections[0]?.content ?? "";
  const hasStructuredOutput =
    sections.length > 0 || metadataEntries.length > 0 || qualityChecks.length > 0 || previewText !== "";
  const formattedJsonBody = isSkeleton ? null : tryFormatJsonBody(body);

  return (
    <BaseNodeWrapper
      nodeType="agent-output"
      selected={selected}
      status={nodeData._status}
      statusMessage={nodeData._statusMessage}
      className={`min-w-[300px] border-amber-500/30 ${isSkeleton ? "opacity-80" : ""}`}
    >
      <Handle
        type="target"
        position={Position.Left}
        id="agent-output-in"
        className="!h-3 !w-3 !bg-amber-500 !border-2 !border-background"
      />

      <div className="flex h-full flex-col gap-3 p-3">
        <header className="space-y-1">
          <div className="flex items-center justify-between gap-2">
            <p className="truncate text-xs font-semibold text-foreground" title={resolvedTitle}>
              {resolvedTitle}
            </p>
            {isSkeleton ? (
                <span className="shrink-0 rounded-full border border-amber-500/50 bg-amber-500/10 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-800 dark:text-amber-200">
                  {t("skeletonBadge")}
                </span>
              ) : null}
            </div>
            {isSkeleton ? (
              <p className="text-[11px] text-amber-700/90 dark:text-amber-300/90">
                {t("plannedOutputLabel")}
                {stepCounter ? ` - ${stepCounter}` : ""}
                {nodeData.stepId ? ` - ${nodeData.stepId}` : ""}
              </p>
            ) : null}
        </header>

        <section
          data-testid="agent-output-meta-strip"
          className="grid grid-cols-2 gap-2 rounded-md border border-border/70 bg-muted/30 px-2 py-1.5"
        >
          <div className="min-w-0">
            <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("channelLabel")}</p>
            <p className="truncate text-xs font-medium text-foreground/90" title={nodeData.channel}>
              {nodeData.channel ?? t("emptyValue")}
            </p>
          </div>
          <div className="min-w-0">
            <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("artifactTypeLabel")}</p>
            <p className="truncate text-xs font-medium text-foreground/90" title={artifactType}>
              {artifactType || t("emptyValue")}
            </p>
          </div>
        </section>

        {isSkeleton ? (
          <section className="space-y-1">
            <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
              {t("bodyLabel")}
            </p>
            <div
              data-testid="agent-output-skeleton-body"
              className="animate-pulse rounded-md border border-dashed border-amber-500/40 bg-gradient-to-r from-amber-500/10 via-amber-500/20 to-amber-500/10 p-3"
            >
              <p className="text-[11px] text-amber-800/90 dark:text-amber-200/90">{t("plannedContent")}</p>
            </div>
          </section>
        ) : hasStructuredOutput ? (
          <>
            {sections.length > 0 ? (
              <section data-testid="agent-output-sections" className="space-y-1.5">
                <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("sectionsLabel")}</p>
                <div className="space-y-1.5">
                  {sections.map((section) => (
                    <div key={section.id} className="rounded-md border border-border/70 bg-background/70 p-2">
                      <p className="text-[11px] font-semibold text-foreground/90">{section.label}</p>
                      <p className="whitespace-pre-wrap break-words text-[12px] leading-relaxed text-foreground/90">
                        {section.content}
                      </p>
                    </div>
                  ))}
                </div>
              </section>
            ) : null}

            {metadataEntries.length > 0 ? (
              <section data-testid="agent-output-metadata" className="space-y-1.5">
                <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("metadataLabel")}</p>
                <div className="space-y-1 text-[12px] text-foreground/90">
                  {metadataEntries.map(([key, value]) => (
                    <p key={key} className="break-words">
                      <span className="font-semibold">{key}</span>: {value}
                    </p>
                  ))}
                </div>
              </section>
            ) : null}

            {qualityChecks.length > 0 ? (
              <section data-testid="agent-output-quality-checks" className="space-y-1.5">
                <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("qualityChecksLabel")}</p>
                <div className="flex flex-wrap gap-1.5">
                  {qualityChecks.map((qualityCheck) => (
                    <span
                      key={qualityCheck}
                      className="rounded-full border border-amber-500/40 bg-amber-500/10 px-2 py-0.5 text-[10px] font-medium text-amber-800 dark:text-amber-200"
                    >
                      {qualityCheck}
                    </span>
                  ))}
                </div>
              </section>
            ) : null}

            <section data-testid="agent-output-preview" className="space-y-1">
              <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">{t("previewLabel")}</p>
              <div className="max-h-40 overflow-auto rounded-md border border-border/70 bg-background/70 p-3 text-[13px] leading-relaxed text-foreground/90">
                <p className="whitespace-pre-wrap break-words">{previewText || t("previewFallback")}</p>
              </div>
            </section>
          </>
        ) : formattedJsonBody ? (
          <section className="space-y-1">
            <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
              {t("bodyLabel")}
            </p>
            <pre
              data-testid="agent-output-json-body"
              className="max-h-48 overflow-auto rounded-md border border-border/80 bg-muted/40 p-3 font-mono text-[11px] leading-relaxed text-foreground/95"
            >
              <code>{formattedJsonBody}</code>
            </pre>
          </section>
        ) : (
          <section className="space-y-1">
            <p className="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
              {t("bodyLabel")}
            </p>
            <div
              data-testid="agent-output-text-body"
              className="max-h-48 overflow-auto rounded-md border border-border/70 bg-background/70 p-3 text-[13px] leading-relaxed text-foreground/90"
            >
              <p className="whitespace-pre-wrap break-words">{body}</p>
            </div>
          </section>
        )}
      </div>
    </BaseNodeWrapper>
  );
}
