"use client";

import { ChangeEvent, FormEvent, KeyboardEvent, useEffect, useMemo, useRef, useState } from "react";
import dynamic from "next/dynamic";

import { ThreeBackdrop } from "@/components/ThreeBackdrop";
import { VoiceInput } from "@/components/VoiceInput";
import { parseSSEStream } from "@/lib/stream";
import { ChatMessage, GenUiPayload } from "@/lib/types";

// Crayon UI's ThemeProvider injects CSS variables that differ between SSR and the
// first client render, which trips Next.js' hydration check. Loading it client-only
// avoids the mismatch without losing any functionality.
const GenUiRenderer = dynamic(
  () => import("@/components/GenUiRenderer").then((mod) => mod.GenUiRenderer),
  { ssr: false }
);

type QuickAction = {
  label: string;
  prompt: string;
};

const QUICK_PROMPTS: QuickAction[] = [
  {
    label: "Find top candidates for senior Python engineer in Bangalore",
    prompt: "Find top candidates for senior Python engineer in Bangalore"
  },
  {
    label: "Check company policies",
    prompt: "Check company policies"
  },
  {
    label: "Create job posting",
    prompt: "Create job posting"
  },
  {
    label: "Schedule interview",
    prompt: "Schedule interview"
  },
  {
    label: "Draft email",
    prompt: "Draft email"
  }
];

const STARTER_ASSISTANT_MESSAGE =
  "Recruitment copilot is ready. Attach resume PDFs or ask for policy checks, candidate ranking, interview scheduling.";

function makeId(): string {
  if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
    return crypto.randomUUID();
  }
  return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
}

function AttachIcon() {
  return (
    <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
      <path
        d="M9.5 17.5l7.1-7.1a3 3 0 10-4.2-4.2l-7.4 7.4a5 5 0 107.1 7.1l6.3-6.3"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function MicIcon() {
  return (
    <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
      <path
        d="M12 3a3 3 0 00-3 3v5a3 3 0 006 0V6a3 3 0 00-3-3zm6 8a1 1 0 112 0 8 8 0 01-7 7.94V21h3a1 1 0 110 2H8a1 1 0 010-2h3v-2.06A8 8 0 014 11a1 1 0 112 0 6 6 0 0012 0z"
        fill="currentColor"
      />
    </svg>
  );
}

function SendIcon() {
  return (
    <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
      <path
        d="M4 12l15-8-3 8 3 8-15-8zm2.8 0l7.8 4.2-1.6-4.2 1.6-4.2L6.8 12z"
        fill="currentColor"
      />
    </svg>
  );
}

function PulseIcon() {
  return (
    <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
      <path
        d="M3 12h4l2.2-4 3.6 8 2.1-4H21"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function PlusIcon() {
  return (
    <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
      <path
        d="M12 5v14M5 12h14"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.9"
        strokeLinecap="round"
      />
    </svg>
  );
}

export function ChatInterface() {
  const [messages, setMessages] = useState<ChatMessage[]>([
    {
      id: makeId(),
      role: "assistant",
      text: STARTER_ASSISTANT_MESSAGE
    }
  ]);
  const [input, setInput] = useState("");
  const [isStreaming, setIsStreaming] = useState(false);
  const [isUploadingFiles, setIsUploadingFiles] = useState(false);
  const [pendingFiles, setPendingFiles] = useState<File[]>([]);
  const [error, setError] = useState<string | null>(null);

  const bottomRef = useRef<HTMLDivElement | null>(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);

  const autoGrow = () => {
    const node = textareaRef.current;
    if (!node) {
      return;
    }
    node.style.height = "0px";
    const next = Math.min(node.scrollHeight, 168);
    node.style.height = `${Math.max(36, next)}px`;
  };

  useEffect(() => {
    autoGrow();
  }, [input]);

  const userTurnCount = useMemo(
    () => messages.filter((item) => item.role === "user").length,
    [messages]
  );
  const assistantTurnCount = useMemo(
    () => messages.filter((item) => item.role === "assistant").length,
    [messages]
  );

  const scrollToBottom = () => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
  };

  const patchAssistant = (assistantId: string, patch: Partial<ChatMessage>) => {
    setMessages((prev) =>
      prev.map((item) => (item.id === assistantId ? { ...item, ...patch } : item))
    );
  };

  const appendAssistantText = (assistantId: string, delta: string) => {
    setMessages((prev) =>
      prev.map((item) => {
        if (item.id !== assistantId) {
          return item;
        }
        return {
          ...item,
          text: `${item.text}${delta}`,
          isStreaming: true
        };
      })
    );
  };

  const sendMessage = async (messageText: string) => {
    const cleaned = messageText.trim();
    if (!cleaned || isStreaming) {
      return;
    }

    setError(null);
    setInput("");

    const userMessage: ChatMessage = {
      id: makeId(),
      role: "user",
      text: cleaned
    };
    const assistantId = makeId();
    const placeholder: ChatMessage = {
      id: assistantId,
      role: "assistant",
      text: "",
      isStreaming: true
    };

    setMessages((prev) => [...prev, userMessage, placeholder]);
    setIsStreaming(true);

    try {
      const response = await fetch("/api/chat", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          message: cleaned,
          user_id: "browser-user",
          session_id: "browser-session",
          top_k: 8
        })
      });

      if (!response.ok || !response.body) {
        const text = await response.text();
        throw new Error(text || "Backend streaming failed.");
      }

      for await (const event of parseSSEStream(response.body)) {
        if (event.event === "token") {
          appendAssistantText(assistantId, String(event.data.delta ?? ""));
          scrollToBottom();
          continue;
        }

        if (event.event === "genui") {
          patchAssistant(assistantId, { genui: event.data as GenUiPayload });
          continue;
        }

        if (event.event === "done") {
          patchAssistant(assistantId, {
            text: String(event.data.message ?? ""),
            isStreaming: false
          });
          continue;
        }

        if (event.event === "error") {
          throw new Error(String(event.data.message ?? "Unknown streaming error"));
        }
      }

      patchAssistant(assistantId, { isStreaming: false });
    } catch (caught) {
      const message = caught instanceof Error ? caught.message : "Unexpected error";
      setError(message);
      patchAssistant(assistantId, {
        text: `Error: ${message}`,
        isStreaming: false
      });
    } finally {
      setIsStreaming(false);
      scrollToBottom();
    }
  };

  const uploadResume = async (file: File) => {
    const formData = new FormData();
    formData.append("file", file);

    const response = await fetch(`/api/upload?session_id=${encodeURIComponent("browser-session")}`, {
      method: "POST",
      body: formData
    });

    const text = await response.text();
    if (!response.ok) {
      throw new Error(text || "Upload failed");
    }

    const payload = JSON.parse(text) as {
      candidate_id: string;
      file_name: string;
      chunks_indexed: number;
      message: string;
    };

    return payload;
  };

  const submitTurn = async (messageText: string) => {
    if (isStreaming || isUploadingFiles) {
      return;
    }

    setError(null);
    const cleaned = messageText.trim();
    let uploadedFileNames: string[] = [];

    if (pendingFiles.length > 0) {
      setIsUploadingFiles(true);
      try {
        const uploadResults = [];
        for (const file of pendingFiles) {
          const result = await uploadResume(file);
          uploadResults.push(result);
        }

        uploadedFileNames = uploadResults.map((item) => item.file_name);

        const profileCards = await Promise.all(
          uploadResults.map(async (item) => {
            try {
              const detailRes = await fetch(
                `/api/candidates/${encodeURIComponent(item.candidate_id)}`
              );
              if (!detailRes.ok) {
                return null;
              }
              const detail = (await detailRes.json()) as { profile?: GenUiPayload };
              return detail.profile?.cards?.[0] ?? null;
            } catch {
              return null;
            }
          })
        );

        const cards = profileCards.filter(
          (card): card is NonNullable<typeof card> => Boolean(card)
        );
        const summary = `Ingested ${uploadResults.length} file(s): ${uploadedFileNames.join(", ")}. Use the cards below to open or shortlist the new candidate(s).`;

        setMessages((prev) => [
          ...prev,
          {
            id: makeId(),
            role: "assistant",
            text: summary,
            genui: cards.length ? { summary, cards } : undefined
          }
        ]);
        setPendingFiles([]);
      } catch (caught) {
        const message = caught instanceof Error ? caught.message : "Upload failed";
        setError(message);
        setIsUploadingFiles(false);
        return;
      } finally {
        setIsUploadingFiles(false);
      }
    }

    const finalPrompt =
      cleaned ||
      (uploadedFileNames.length > 0
        ? `I uploaded these resumes: ${uploadedFileNames.join(", ")}. Please shortlist top candidates now.`
        : "");

    if (!finalPrompt) {
      return;
    }

    await sendMessage(finalPrompt);
  };

  const onFileSelection = (event: ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(event.target.files ?? []);
    const validPdfFiles = files.filter((file) => file.name.toLowerCase().endsWith(".pdf"));
    const invalidCount = files.length - validPdfFiles.length;

    if (invalidCount > 0) {
      setError("Only PDF files are supported for resume upload.");
    }

    if (validPdfFiles.length > 0) {
      setPendingFiles((prev) => {
        const merged = [...prev, ...validPdfFiles];
        const seen = new Set<string>();
        return merged.filter((file) => {
          const key = `${file.name}-${file.size}-${file.lastModified}`;
          if (seen.has(key)) {
            return false;
          }
          seen.add(key);
          return true;
        });
      });
    }

    event.target.value = "";
  };

  const removePendingFile = (target: File) => {
    setPendingFiles((prev) =>
      prev.filter(
        (file) =>
          !(
            file.name === target.name &&
            file.size === target.size &&
            file.lastModified === target.lastModified
          )
      )
    );
  };

  const onSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    void submitTurn(input);
  };

  const startNewChat = () => {
    if (isStreaming || isUploadingFiles) {
      return;
    }

    setMessages([
      {
        id: makeId(),
        role: "assistant",
        text: STARTER_ASSISTANT_MESSAGE
      }
    ]);
    setPendingFiles([]);
    setInput("");
    setError(null);
  };

  return (
    <main className="workspace-shell">
      <div className="three-layer" aria-hidden="true">
        <ThreeBackdrop />
      </div>

      <div className="workspace-frame">
        <aside className="left-sidebar">
          <div className="sidebar-brand">
            <p className="brand-eyebrow">Recruitment OS</p>
            <h1>Control Hub</h1>
            <p>
              Enterprise assistant for candidate search, policy guidance, interview operations, and posting
              generation.
            </p>
          </div>

          <button
            type="button"
            className="sidebar-new-chat"
            disabled={isStreaming || isUploadingFiles}
            onClick={startNewChat}
          >
            <PlusIcon />
            <span>New chat</span>
          </button>

          <section className="sidebar-section" aria-label="Quick actions">
            <h2>Quick Actions</h2>
            <div className="sidebar-prompt-list">
              {QUICK_PROMPTS.map((action) => (
                <button
                  key={action.label}
                  type="button"
                  className="sidebar-prompt"
                  disabled={isStreaming || isUploadingFiles}
                  onClick={() => {
                    void submitTurn(action.prompt);
                  }}
                >
                  {action.label}
                </button>
              ))}
            </div>
          </section>

          <section className="sidebar-section">
            <h2>Session</h2>
            <p className="sidebar-meta">User turns: {userTurnCount}</p>
            <p className="sidebar-meta">Assistant turns: {assistantTurnCount}</p>
            <p className="sidebar-meta">State: {isStreaming ? "Streaming response" : "Idle"}</p>
            <p className="sidebar-meta">Queued resumes: {pendingFiles.length}</p>
          </section>
        </aside>

        <section className="chat-main">
          <header className="chat-header">
            <h2>Recruitment Copilot</h2>
            <span className={`runtime-pill ${isStreaming ? "busy" : "ready"}`}>
              {isStreaming ? "Live stream" : "Realtime connected"}
            </span>
          </header>

          <div className="chatbox-shell">
            <section className="conversation-surface">
              <div className="messages">
                {messages.map((message) => {
                  const showThinking =
                    message.role === "assistant" &&
                    message.isStreaming &&
                    !message.text.trim() &&
                    !message.genui;
                  return (
                    <div key={message.id} className={`message-row ${message.role}`}>
                      <div className="bubble">
                        {message.role === "assistant" ? (
                          showThinking ? (
                            <span className="assistant-thinking" aria-label="Thinking">
                              <span className="dot" />
                              <span className="dot" />
                              <span className="dot" />
                            </span>
                          ) : (
                            <GenUiRenderer
                              text={message.text}
                              payload={message.genui}
                              isStreaming={Boolean(message.isStreaming)}
                              onAction={(llmFriendlyMessage, humanFriendlyMessage) => {
                                void sendMessage(llmFriendlyMessage || humanFriendlyMessage);
                              }}
                            />
                          )
                        ) : (
                          <p className="user-message-text">{message.text}</p>
                        )}
                      </div>
                    </div>
                  );
                })}
                <div ref={bottomRef} />
              </div>
            </section>

            <section className="composer-dock">
              {pendingFiles.length > 0 ? (
                <div className="pending-files" aria-label="Pending file uploads">
                  {pendingFiles.map((file) => (
                    <div
                      key={`${file.name}-${file.size}-${file.lastModified}`}
                      className="file-chip"
                      title={file.name}
                    >
                      <span className="file-chip-name">{file.name}</span>
                      <button
                        type="button"
                        className="file-chip-remove"
                        onClick={() => removePendingFile(file)}
                      >
                        x
                      </button>
                    </div>
                  ))}
                </div>
              ) : null}

              <form onSubmit={onSubmit}>
                <div className="thesys-input-shell">
                  <button
                    type="button"
                    className="icon-btn"
                    title="Attach PDF resumes"
                    disabled={isStreaming || isUploadingFiles}
                    onClick={() => fileInputRef.current?.click()}
                  >
                    <AttachIcon />
                  </button>

                  <textarea
                    ref={textareaRef}
                    className="text-input compact"
                    value={input}
                    rows={1}
                    disabled={isStreaming || isUploadingFiles}
                    placeholder="Message the recruitment copilot"
                    onChange={(event) => setInput(event.target.value)}
                    onKeyDown={(event) => {
                      if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
                        event.preventDefault();
                        void submitTurn(input);
                      }
                    }}
                  />

                  <VoiceInput
                    className="icon-btn"
                    listeningContent={<PulseIcon />}
                    title="Speak your prompt"
                    disabled={isStreaming || isUploadingFiles}
                    onTranscript={(text) => {
                      setInput((prev) => (prev ? `${prev} ${text}` : text));
                    }}
                  >
                    <MicIcon />
                  </VoiceInput>

                  <button
                    className="send-btn-round"
                    type="submit"
                    title="Send message"
                    disabled={isStreaming || isUploadingFiles || (!input.trim() && pendingFiles.length === 0)}
                  >
                    {isStreaming ? <PulseIcon /> : <SendIcon />}
                  </button>
                </div>

                <input
                  ref={fileInputRef}
                  type="file"
                  accept="application/pdf"
                  multiple
                  style={{ display: "none" }}
                  onChange={onFileSelection}
                />
              </form>

              {isUploadingFiles ? (
                <div className="dock-foot">
                  <span className="side-copy">Uploading attached resumes…</span>
                </div>
              ) : null}

              {error ? <p className="error-copy">{error}</p> : null}
            </section>
          </div>
        </section>
      </div>
    </main>
  );
}
