import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { browseProjectFolders, createProjectFolder } from "./project-api.js";
import { filterProjectFolders, projectOperationError, projectPopoverPlacement, upsertProjectFolder, validateProjectName } from "./project-picker.js";
import { clearNewChatDraft, loadNewChatDraft, saveNewChatDraft } from "./new-chat-draft.js";
import { useI18n } from "../../shared/I18nProvider.jsx";
import { useBackLayer } from "../../app/useBackLayer.js";
import { ComposerModelMenu } from "../conversation/ComposerModelMenu.jsx";
import { ComposerPermissionMenu } from "../conversation/ComposerPermissionMenu.jsx";
import { ComposerToolsMenu } from "../conversation/ComposerToolsMenu.jsx";
import { BackIcon, CloseIcon, FolderIcon, ForwardIcon, MicrophoneIcon, NoProjectIcon, PlusIcon, SendIcon, StopIcon } from "../../shared/ActionIcons.jsx";
import { buildComposerPayload, loadComposerOptions, normalizeComposerModel, updateComposerOption } from "../conversation/composer-state.js";
import { shouldSubmitComposerKey, usesCompactComposerKeyboard } from "../conversation/composer-keyboard.js";
import { resizeComposerInput } from "../conversation/composer-resize.js";
import { appendVoiceTranscript, buildVoiceWaveBars, createVoiceRecognition, supportsVoiceInput, voiceInputErrorKey } from "../conversation/voice-input.js";
import { selectAttachmentUploads } from "../conversation/attachment-policy.js";

function VoiceWaveform({ bars }) {
  return <div className="voice-waveform" aria-hidden="true">
    {bars.map((bar, index) => <span className="voice-waveform-bar" key={index} style={{ "--voice-level": bar }} />)}
  </div>;
}

export function NewChatPanel({ selectedWorkspace, recentProjects = [], capabilities, selectedSkills, onBack, onWorkspaceChange, onSelectProject, onSelectFolder, onCreateWithMessage, onCreateWithoutProject, onToggleSkill }) {
  const { t } = useI18n();
  const [initialDraft] = useState(() => loadNewChatDraft());
  const [composerOptions, setComposerOptions] = useState(() => loadComposerOptions(globalThis.localStorage));
  const [projects, setProjects] = useState(recentProjects);
  const [search, setSearch] = useState(initialDraft.search);
  const [newProject, setNewProject] = useState(initialDraft.newProject);
  const [name, setName] = useState(initialDraft.name);
  const [pending, setPending] = useState("");
  const [error, setError] = useState("");
  const [folderBrowser, setFolderBrowser] = useState(null);
  const [browserFolderCreate, setBrowserFolderCreate] = useState(false);
  const [browserFolderName, setBrowserFolderName] = useState("");
  const [pickerOpen, setPickerOpen] = useState(false);
  const [projectMenuOpen, setProjectMenuOpen] = useState(false);
  const [projectMenuPlacement, setProjectMenuPlacement] = useState("below");
  const [selectedWorkspaceId, setSelectedWorkspaceId] = useState(selectedWorkspace?.id ?? "");
  const [selectedProjectName, setSelectedProjectName] = useState(selectedWorkspace?.label ?? "");
  const [message, setMessage] = useState("");
  const [attachments, setAttachments] = useState([]);
  const [messagePending, setMessagePending] = useState(false);
  const [messageError, setMessageError] = useState("");
  const activeRef = useRef(true);
  const attachmentsRef = useRef([]);
  const selectionTouchedRef = useRef(false);
  const browseRequestRef = useRef(0);
  const inputRef = useRef(null);
  const fileRef = useRef(null);
  const voiceRef = useRef(null);
  const voiceSubmitPendingRef = useRef(false);
  const voiceCompletionActionRef = useRef("");
  const voiceBaseMessageRef = useRef("");
  const messageRef = useRef(message);
  messageRef.current = message;
  const [voiceSupported] = useState(() => supportsVoiceInput());
  const [voiceStatus, setVoiceStatus] = useState("");
  const [voiceDiagnostic, setVoiceDiagnostic] = useState("");
  const [voiceBars, setVoiceBars] = useState(() => buildVoiceWaveBars(0));
  useBackLayer(pickerOpen, () => setPickerOpen(false), "project-picker", { preferTop: true });
  useBackLayer(projectMenuOpen, () => setProjectMenuOpen(false), "new-project-menu", { preferTop: true });
  useBackLayer(newProject, () => setNewProject(false), "new-project-form", { preferTop: true });
  useBackLayer(Boolean(folderBrowser) && !browserFolderCreate, () => folderBrowser?.parentId ? browse(folderBrowser.parentId) : setFolderBrowser(null), "folder-browser", { preferTop: true });
  useBackLayer(browserFolderCreate, () => setBrowserFolderCreate(false), "folder-create", { preferTop: true });

  function dismissPickerOutside(event) {
    if ((projectMenuOpen || newProject) && !event.target.closest(".project-picker-new-project")) {
      setProjectMenuOpen(false);
      setNewProject(false);
    }
    if (pickerOpen && !event.target.closest(".project-picker, .project-picker-trigger")) setPickerOpen(false);
  }

  function toggleProjectMenu(event) {
    if (projectMenuOpen) {
      setProjectMenuOpen(false);
      return;
    }
    setProjectMenuPlacement(projectPopoverPlacement(event.currentTarget.getBoundingClientRect(), window.innerHeight));
    setNewProject(false);
    setProjectMenuOpen(true);
  }

  useEffect(() => {
    activeRef.current = true;
    return () => {
      activeRef.current = false;
      voiceCompletionActionRef.current = "draft";
      voiceRef.current?.stop();
      for (const item of attachmentsRef.current) URL.revokeObjectURL(item.previewUrl);
    };
  }, []);

  useEffect(() => { setProjects(recentProjects); }, [recentProjects]);

  useEffect(() => { attachmentsRef.current = attachments; }, [attachments]);

  useEffect(() => {
    const models = capabilities?.models ?? [];
    if (models.length === 0) return;
    setComposerOptions((current) => normalizeComposerModel(current, models));
  }, [capabilities?.models]);

  useEffect(() => {
    saveNewChatDraft(undefined, { search, newProject, name });
  }, [name, newProject, search]);

  useEffect(() => {
    if (pickerOpen) return;
    setProjectMenuOpen(false);
    setNewProject(false);
    setFolderBrowser(null);
    setBrowserFolderCreate(false);
    setBrowserFolderName("");
    browseRequestRef.current += 1;
  }, [pickerOpen]);

  useEffect(() => {
    if (selectionTouchedRef.current || !selectedWorkspace?.id) return;
    setSelectedWorkspaceId(selectedWorkspace.id);
    setSelectedProjectName(selectedWorkspace.label);
  }, [selectedWorkspace?.id, selectedWorkspace?.label]);

  function finish(result) {
    if (result === false) {
      if (activeRef.current) setPending("");
      return false;
    }
    clearNewChatDraft();
    return true;
  }

  async function open(project) {
    setPending(project.name);
    setError("");
    try {
      selectionTouchedRef.current = true;
      const workspaceId = project.workspaceId
        ? project.workspaceId
        : await onSelectProject({ name: project.name, create: false });
      if (!workspaceId) return finish(false);
      setSelectedWorkspaceId(workspaceId);
      setSelectedProjectName(project.name);
      setProjects((current) => upsertProjectFolder(current, { name: project.name, workspaceId }));
      setSearch("");
      onWorkspaceChange(workspaceId);
      setPickerOpen(false);
      setPending("");
    } catch (openError) {
      if (activeRef.current) {
        setError(projectOperationError(openError));
        setPending("");
      }
    }
  }

  async function create() {
    const trimmed = name.trim();
    const validation = validateProjectName(trimmed);
    if (!validation.valid) {
      setError(validation.message);
      return;
    }
    setPending(trimmed);
    setError("");
    try {
      selectionTouchedRef.current = true;
      const workspaceId = await onSelectProject({ name: trimmed, create: true });
      if (!workspaceId) return finish(false);
      setSelectedWorkspaceId(workspaceId);
      setSelectedProjectName(trimmed);
      setProjects((current) => upsertProjectFolder(current, { name: trimmed, workspaceId }));
      setSearch("");
      setName("");
      setNewProject(false);
      onWorkspaceChange(workspaceId);
      setPickerOpen(false);
      setPending("");
    } catch (createError) {
      if (activeRef.current) {
        setError(projectOperationError(createError));
        setPending("");
      }
    }
  }

  async function createWithoutProject() {
    setPending("without-project");
    setError("");
    try {
      finish(await onCreateWithoutProject(composerOptions));
    } catch (createError) {
      if (activeRef.current) {
        setError(projectOperationError(createError));
        setPending("");
      }
    }
  }

  function setComposerOption(key, value) {
    setComposerOptions((current) => updateComposerOption(current, key, value, globalThis.localStorage));
  }

  async function browse(folderId = "") {
    const requestId = ++browseRequestRef.current;
    setPending("browse");
    setError("");
    try {
      const result = await browseProjectFolders(folderId);
      if (!activeRef.current || requestId !== browseRequestRef.current) return;
      setFolderBrowser(result);
      setBrowserFolderCreate(false);
      setBrowserFolderName("");
    } catch (browseError) {
      if (!activeRef.current || requestId !== browseRequestRef.current) return;
      if (browseError?.message === "folder_selection_expired") setFolderBrowser(null);
      setError(projectOperationError(browseError));
    } finally {
      if (activeRef.current && requestId === browseRequestRef.current) setPending("");
    }
  }

  async function createFolderHere() {
    if (!folderBrowser?.currentId) return;
    const trimmed = browserFolderName.trim();
    const validation = validateProjectName(trimmed);
    if (!validation.valid) {
      setError(validation.message);
      return;
    }
    const requestId = ++browseRequestRef.current;
    setPending("create-folder");
    setError("");
    try {
      const result = await createProjectFolder(folderBrowser.currentId, trimmed);
      if (!activeRef.current || requestId !== browseRequestRef.current) return;
      setFolderBrowser(result);
      selectionTouchedRef.current = true;
      const workspaceId = await onSelectFolder(result.currentId);
      if (!activeRef.current || requestId !== browseRequestRef.current || !workspaceId) return finish(false);
      setSelectedWorkspaceId(workspaceId);
      setSelectedProjectName(result.current);
      setProjects((current) => upsertProjectFolder(current, { name: result.current, workspaceId }));
      setSearch("");
      setBrowserFolderCreate(false);
      setBrowserFolderName("");
      onWorkspaceChange(workspaceId);
      setPending("");
      setPickerOpen(false);
    } catch (createError) {
      if (activeRef.current && requestId === browseRequestRef.current) setError(projectOperationError(createError));
    } finally {
      if (activeRef.current && requestId === browseRequestRef.current) setPending("");
    }
  }

  async function selectFolder() {
    if (!folderBrowser?.currentId) return;
    setPending("select-folder");
    setError("");
    try {
      selectionTouchedRef.current = true;
      const workspaceId = await onSelectFolder(folderBrowser.currentId);
      if (!workspaceId) return finish(false);
      setSelectedWorkspaceId(workspaceId);
      setSelectedProjectName(folderBrowser.current);
      setProjects((current) => upsertProjectFolder(current, { name: folderBrowser.current, workspaceId }));
      setSearch("");
      onWorkspaceChange(workspaceId);
      setPickerOpen(false);
      setPending("");
    } catch (selectError) {
      setError(projectOperationError(selectError));
      setPending("");
    }
  }

  async function submitMessage(event) {
    event?.preventDefault();
    if (voiceStatus === "listening") return finishVoice("send");
    const hasComposerContent = message.trim().length > 0 || attachments.length > 0;
    if (!hasComposerContent || !selectedWorkspaceId || messagePending) return;
    setMessagePending(true);
    setMessageError("");
    try {
      await onCreateWithMessage(
        selectedWorkspaceId,
        message.trim(),
        buildComposerPayload({ message: message.trim(), options: composerOptions, selectedSkills }),
        attachments.map((item) => item.file)
      );
      clearNewChatDraft();
    } catch (submitError) {
      setMessageError(projectOperationError(submitError));
      setMessagePending(false);
    }
  }

  function selectAttachments(files) {
    const selection = selectAttachmentUploads(attachments, files);
    const selected = selection.accepted.map((file) => ({
      id: crypto.randomUUID(),
      file,
      name: file.name,
      size: file.size,
      previewUrl: URL.createObjectURL(file)
    }));
    if (selection.rejected.length) setMessageError(projectOperationError(new Error("attachment_limit_exceeded")));
    setAttachments((current) => [...current, ...selected]);
  }

  function removeAttachment(id) {
    setAttachments((current) => {
      const removed = current.find((item) => item.id === id);
      if (removed) URL.revokeObjectURL(removed.previewUrl);
      return current.filter((item) => item.id !== id);
    });
  }

  function startVoice() {
    if (!voiceSupported || !selectedWorkspaceId || messagePending) return;
    setMessageError("");
    setVoiceDiagnostic("starting");
    setVoiceBars(buildVoiceWaveBars(0));
    voiceSubmitPendingRef.current = false;
    voiceCompletionActionRef.current = "";
    voiceBaseMessageRef.current = messageRef.current;
    const voice = createVoiceRecognition({
      onDiagnostic: (status) => setVoiceDiagnostic(status),
      onTranscript: (text) => {
        const nextMessage = appendVoiceTranscript(voiceBaseMessageRef.current, text);
        messageRef.current = nextMessage;
        setMessage(nextMessage);
      },
      onError: (voiceError) => {
        voiceSubmitPendingRef.current = false;
        voiceCompletionActionRef.current = "";
        setVoiceDiagnostic("");
        setMessageError(t(voiceInputErrorKey(voiceError)));
        setVoiceStatus("");
        setVoiceBars(buildVoiceWaveBars(0));
      },
      onEnd: () => {
        const shouldSend = voiceCompletionActionRef.current === "send";
        voiceCompletionActionRef.current = "";
        setVoiceStatus("");
        voiceRef.current = null;
        if (shouldSend && messageRef.current.trim()) voiceSubmitPendingRef.current = true;
        setVoiceBars(buildVoiceWaveBars(0));
        requestAnimationFrame(() => inputRef.current?.focus());
      }
    });
    if (!voice.supported) {
      setVoiceDiagnostic("error:not_supported");
      return;
    }
    voiceRef.current = voice;
    setVoiceStatus("listening");
    try {
      voice.start();
    } catch {
      setVoiceDiagnostic("error:start_failed");
      setVoiceStatus("");
      voiceRef.current = null;
      setVoiceBars(buildVoiceWaveBars(0));
    }
  }

  function finishVoice(action = "draft") {
    if (voiceCompletionActionRef.current) return;
    const voice = voiceRef.current;
    if (!voice) return;
    voiceCompletionActionRef.current = action;
    voice.stop();
  }

  useLayoutEffect(() => {
    resizeComposerInput(inputRef.current);
  }, [message]);

  useEffect(() => {
    if (!voiceSubmitPendingRef.current || !message.trim() || messagePending || !selectedWorkspaceId) return;
    voiceSubmitPendingRef.current = false;
    void submitMessage();
  }, [message, voiceStatus, messagePending, selectedWorkspaceId]);

  const visibleProjects = filterProjectFolders(projects, search);
  const displayedProjectName = selectedProjectName || t("newChat.chooseProject");
  const models = capabilities?.models ?? [];
  const skills = capabilities?.skills ?? [];
  const hasComposerContent = message.trim().length > 0 || attachments.length > 0;
  const listening = voiceStatus === "listening";
  const showVoiceButton = voiceSupported && !hasComposerContent && !listening;
  return <div className="new-chat-overlay" role="presentation">
    <section className="new-chat-panel" role="dialog" aria-labelledby="new-chat-title" onPointerDown={dismissPickerOutside}>
      <button type="button" className="screen-back conversation-back" aria-label={t("newChat.back")} onClick={onBack}><BackIcon /></button>
      <div className="project-picker-shell">
        <div className="project-picker-copy">
          <h2 id="new-chat-title" className="project-picker-prompt">
            <span className="new-chat-prompt-question">{t("newChat.promptBefore")}</span>
            <button type="button" className="project-picker-trigger" aria-expanded={pickerOpen} onClick={() => setPickerOpen((open) => !open)}>{displayedProjectName}</button>
          </h2>
        </div>
        {pickerOpen && <div className="project-picker">
          {folderBrowser ? <div className="folder-browser">
            <div className="folder-browser-header"><button type="button" onClick={() => browserFolderCreate ? setBrowserFolderCreate(false) : folderBrowser.parentId ? browse(folderBrowser.parentId) : setFolderBrowser(null)}>{t("newChat.folderUp")}</button><strong>{folderBrowser.current}</strong></div>
            <div className="folder-browser-list">{folderBrowser.folders.map((folder) => <button type="button" key={folder.id} disabled={Boolean(pending)} onClick={() => browse(folder.id)}><FolderIcon className="project-picker-icon" /><span>{folder.name}</span><ForwardIcon className="project-picker-chevron-svg" /></button>)}</div>
            {folderBrowser.currentId && <div className="folder-browser-actions">
              {browserFolderCreate ? <div className="folder-browser-create-form">
                <input value={browserFolderName} onChange={(event) => setBrowserFolderName(event.target.value)} placeholder={t("newChat.folderName")} aria-label={t("newChat.folderName")} maxLength={80} />
                <button type="button" disabled={!browserFolderName.trim() || Boolean(pending)} onClick={createFolderHere}>{t("newChat.confirmCreateFolder")}</button>
              </div> : <button type="button" className="folder-browser-create" disabled={Boolean(pending)} onClick={() => setBrowserFolderCreate(true)}>{t("newChat.createFolderHere")}</button>}
              <button type="button" className="folder-browser-select" disabled={Boolean(pending)} onClick={selectFolder}>{t("newChat.selectThisFolder")}</button>
            </div>}
          </div> : <>
          <h3 className="project-picker-title">{t("newChat.chooseProject")}</h3>
          <button type="button" className="project-picker-without-project" disabled={Boolean(pending)} onClick={createWithoutProject}><NoProjectIcon className="project-picker-action-svg" /><span>{t("newChat.withoutProject")}</span></button>
          <div className="project-picker-actions project-picker-new-project">
            <button type="button" aria-expanded={projectMenuOpen} onClick={toggleProjectMenu}><PlusIcon /><span>{t("newChat.newProject")}</span><ForwardIcon className={`project-picker-chevron-svg${projectMenuOpen ? " open" : ""}`} /></button>
            {projectMenuOpen && <div className={`project-create-menu project-create-popover project-create-popover--${projectMenuPlacement}`} role="menu">
              <button type="button" role="menuitem" onClick={() => { setProjectMenuOpen(false); setNewProject(true); }}><PlusIcon /><span>{t("newChat.createFolder")}</span></button>
              <button type="button" role="menuitem" disabled={Boolean(pending)} onClick={() => { setProjectMenuOpen(false); setNewProject(false); browse(); }}><FolderIcon className="project-picker-action-svg" /><span>{t("newChat.browseFolders")}</span></button>
            </div>}
            {newProject && !projectMenuOpen && <div className={`new-project-form project-create-popover project-create-popover--${projectMenuPlacement}`}>
              <input value={name} onChange={(event) => setName(event.target.value)} placeholder={t("newChat.folderName")} aria-label={t("newChat.folderName")} maxLength={80} />
              <button type="button" disabled={!name.trim() || Boolean(pending)} onClick={create}>{t("newChat.create")}</button>
            </div>}
          </div>
          <h3 className="project-picker-section-title">{t("newChat.recentProjects")}</h3>
          <div className="project-picker-list">{visibleProjects.map((project) => <button type="button" key={project.name} aria-current={project.workspaceId === selectedWorkspaceId ? "true" : undefined} disabled={Boolean(pending)} onClick={() => open(project)}>
            <FolderIcon className="project-picker-icon" /><strong>{project.name}</strong>
          </button>)}</div>
          {projects.length === 0 && !error && <p>{t("newChat.noRecentProjects")}</p>}
          </>}{error && <p className="thread-state error" role="alert">{error}</p>}
          {!folderBrowser && <input className="project-search" value={search} onChange={(event) => setSearch(event.target.value)} placeholder={t("newChat.search")} aria-label={t("newChat.search")} />}
        </div>}
      </div>
      <form className="new-chat-composer composer-dock" onSubmit={submitMessage}>
        <div className="composer">
        <div className="composer-quick-options">
          <ComposerModelMenu models={models} options={composerOptions} disabled={messagePending} onOption={setComposerOption} />
          <ComposerPermissionMenu permissions={capabilities?.permissions ?? []} options={composerOptions} disabled={messagePending} onOption={setComposerOption} />
          {composerOptions.planMode && <button className="composer-pill composer-active-chip" type="button" disabled={messagePending} onClick={() => setComposerOption("planMode", false)}><span>{t("conversation.plan")} <CloseIcon /></span></button>}
          {selectedSkills.map((skill) => <button className="composer-pill composer-active-chip" key={skill.name} type="button" disabled={messagePending} onClick={() => onToggleSkill(skill)}><span>{skill.displayName || skill.name} <CloseIcon /></span></button>)}
        </div>
        {attachments.length > 0 && <div className="composer-attachments">
          {attachments.map((item) => <div className="composer-attachment-card" key={item.id}>
            <img className="composer-attachment-preview" src={item.previewUrl} alt={item.name || t("conversation.attachedImage")} />
            <button className="composer-attachment-remove" type="button" onClick={() => removeAttachment(item.id)} aria-label={t("conversation.removeImage", { name: item.name || t("conversation.image") })}><CloseIcon /></button>
          </div>)}
        </div>}
        <div className={`composer-row ${listening ? "listening" : ""}`}>
          {!listening && <ComposerToolsMenu busy={messagePending} uploadDisabled={messagePending} fileRef={fileRef} planMode={composerOptions.planMode} skills={skills} selectedSkills={selectedSkills} onOption={setComposerOption} onToggleSkill={onToggleSkill} />}
          <input ref={fileRef} className="composer-file" type="file" accept="image/*" multiple onChange={(event) => { const files = Array.from(event.target.files ?? []); event.target.value = ""; selectAttachments(files); }} />
          <div className={`composer-input-wrap ${listening ? "composer-input-wrap--listening" : ""}`}>
            {listening ? <div className="composer-voice-surface">
              <VoiceWaveform bars={voiceBars} />
              {message.trim() && <span className="voice-transcript">{message.trim()}</span>}
              <button className="composer-voice-stop" type="button" aria-label={t("conversation.stopVoice")} onClick={() => finishVoice("draft")}><span className="composer-stop-icon composer-action-icon" aria-hidden="true"><StopIcon /></span></button>
            </div> : <textarea className="composer-input" ref={inputRef} rows="1" value={message} onChange={(event) => { setMessageError(""); setMessage(event.target.value); }} placeholder={t("newChat.message")} aria-label={t("newChat.message")} disabled={messagePending} onKeyDown={(event) => { if (shouldSubmitComposerKey(event, { compact: usesCompactComposerKeyboard() })) { event.preventDefault(); void submitMessage(); } }} />}
          </div>
          {showVoiceButton ? <button className="composer-voice" type="button" aria-label={t("conversation.startVoice")} onClick={startVoice} disabled={!selectedWorkspaceId || messagePending}><span className="composer-mic-icon composer-action-icon" aria-hidden="true"><MicrophoneIcon /></span></button> : <button type={listening ? "button" : "submit"} className="composer-send" aria-label={t("conversation.send")} onClick={listening ? () => finishVoice("send") : undefined} disabled={!listening && (!hasComposerContent || !selectedWorkspaceId || messagePending)}><span className="composer-send-icon composer-action-icon" aria-hidden="true"><SendIcon /></span></button>}
        </div>
        {voiceDiagnostic && <span className="voice-diagnostic">{voiceDiagnostic}</span>}
        {messageError && <p className="send-error" role="alert">{messageError}</p>}
        </div>
      </form>
    </section>
  </div>;
}
