"use client";

import { useCallback, useEffect, useRef, useState } from "react";

import { Button } from "@/components/ui/button";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import type { VoicePreview } from "@/hooks/use-voice-preview";

const SHOW_MESSAGE_TOOL = "show_message";
const MAX_ACTIONS = 5;

type MessageActionStyle = "primary" | "secondary" | "danger";

type MessageAction = {
  id: string;
  label: string;
  style: MessageActionStyle;
};

type MessageState = {
  title: string;
  message: string;
  actions: MessageAction[];
  dismissible: boolean;
};

type PendingMessage = {
  resolve: (result: { action: string }) => void;
  reject: (error: Error) => void;
};

function requiredString(
  value: unknown,
  field: string,
  maxLength: number,
): string {
  if (typeof value !== "string" || !value.trim()) {
    throw new Error(`${field} 必须是非空字符串`);
  }
  const normalized = value.trim();
  if (normalized.length > maxLength) {
    throw new Error(`${field} 最多允许 ${maxLength} 个字符`);
  }
  return normalized;
}

function normalizeAction(value: unknown, index: number): MessageAction {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new Error(`actions[${index}] 格式不正确`);
  }
  const action = value as Record<string, unknown>;
  const rawStyle = action.style ?? "secondary";
  if (
    rawStyle !== "primary" &&
    rawStyle !== "secondary" &&
    rawStyle !== "danger"
  ) {
    throw new Error(`actions[${index}].style 不受支持`);
  }
  return {
    id: requiredString(action.id, `actions[${index}].id`, 64),
    label: requiredString(action.label, `actions[${index}].label`, 40),
    style: rawStyle,
  };
}

function normalizeMessage(argumentsValue: Record<string, unknown>): MessageState {
  const rawActions = argumentsValue.actions;
  if (
    rawActions !== undefined &&
    (!Array.isArray(rawActions) || rawActions.length > MAX_ACTIONS)
  ) {
    throw new Error(`actions 必须是数组且最多包含 ${MAX_ACTIONS} 项`);
  }
  const actions =
    Array.isArray(rawActions) && rawActions.length > 0
      ? rawActions.map(normalizeAction)
      : [
          {
            id: "acknowledged",
            label: "知道了",
            style: "primary" as const,
          },
        ];
  if (new Set(actions.map((action) => action.id)).size !== actions.length) {
    throw new Error("actions.id 不能重复");
  }
  if (
    argumentsValue.dismissible !== undefined &&
    typeof argumentsValue.dismissible !== "boolean"
  ) {
    throw new Error("dismissible 必须是布尔值");
  }

  return {
    title:
      argumentsValue.title === undefined
        ? "提示"
        : requiredString(argumentsValue.title, "title", 120),
    message: requiredString(argumentsValue.message, "message", 2_000),
    actions,
    dismissible: argumentsValue.dismissible !== false,
  };
}

function actionVariant(style: MessageActionStyle) {
  if (style === "primary") return "default" as const;
  if (style === "danger") return "destructive" as const;
  return "outline" as const;
}

/**
 * Renders messages requested by the agent through the generic Client Tool
 * channel. The tool result is held until the user chooses an action.
 */
export function ClientMessageDialog({
  preview,
  container,
  contained = false,
}: {
  preview: VoicePreview;
  container?: HTMLElement | null;
  contained?: boolean;
}) {
  const { registerClientTool, status } = preview;
  const [message, setMessage] = useState<MessageState | null>(null);
  const pendingRef = useRef<PendingMessage | null>(null);

  const complete = useCallback((action: string) => {
    const pending = pendingRef.current;
    if (!pending) return;
    pendingRef.current = null;
    setMessage(null);
    pending.resolve({ action });
  }, []);

  useEffect(() => {
    const unregister = registerClientTool(SHOW_MESSAGE_TOOL, (argumentsValue) => {
      if (pendingRef.current) {
        throw new Error("已有消息弹窗正在等待用户操作");
      }
      const nextMessage = normalizeMessage(argumentsValue);
      return new Promise<{ action: string }>((resolve, reject) => {
        pendingRef.current = { resolve, reject };
        setMessage(nextMessage);
      });
    });

    return () => {
      unregister();
      const pending = pendingRef.current;
      pendingRef.current = null;
      pending?.reject(new Error("消息弹窗已关闭"));
    };
  }, [registerClientTool]);

  useEffect(() => {
    if (status === "connected" || !pendingRef.current) return;
    const pending = pendingRef.current;
    pendingRef.current = null;
    pending.reject(new Error("会话已结束"));
    setMessage(null);
  }, [status]);

  return (
    <Dialog
      modal={!contained}
      open={message !== null}
      onOpenChange={(open) => {
        if (!open && message?.dismissible) complete("dismissed");
      }}
    >
      <DialogContent
        className="gap-5 sm:max-w-md"
        portalContainer={container}
        contained={contained}
        showCloseButton={message?.dismissible ?? true}
        onEscapeKeyDown={(event) => {
          if (!message?.dismissible) event.preventDefault();
        }}
        onInteractOutside={(event) => {
          if (!message?.dismissible) event.preventDefault();
        }}
      >
        <DialogHeader className="text-left">
          <DialogTitle>{message?.title}</DialogTitle>
          <DialogDescription className="whitespace-pre-wrap leading-6 text-body">
            {message?.message}
          </DialogDescription>
        </DialogHeader>
        <DialogFooter>
          {message?.actions.map((action) => (
            <Button
              key={action.id}
              type="button"
              variant={actionVariant(action.style)}
              onClick={() => complete(action.id)}
            >
              {action.label}
            </Button>
          ))}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
