import React, { useRef, useState, useEffect } from 'react';
import TextEditor, { type TextEditorRef } from './TextEditor.js';
import FormLabel from './FormLabel.js';
import Lightbulb from './Lightbulb.js';
import { Menu, Pen, Sparkles, Info, Bug } from 'lucide-react';
import useStream from '../utilities/useStream.js';
import { marked } from 'marked';
import { Copy } from 'lucide-react';
import Button from './Button.js';
import TagInput from './TagInput.js';
import './ContentGenerator.css';

marked.use({ async: false });

export default function ContentGenerator({
  draftId,
  initialText = '',
  setText = (_) => {},
}: {
  draftId: string | undefined;
  initialText?: string;
  setText?: (text: string) => void;
}) {
  const menuRef = useRef<HTMLDivElement>(null);
  const [menuOpen, setMenuOpen] = useState(false);
  const [draftOptionsOpen, setDraftOptionsOpen] = useState(false);
  const openDraftOptions = () => {
    setMenuOpen(false);
    setDraftOptionsOpen(true);
  };
  const closeMenu = () => {
    setDraftOptionsOpen(false);
    setMenuOpen(false);
  };
  const toggleMenu = () => {
    if (draftOptionsOpen) {
      closeMenu();
    } else {
      setMenuOpen(!menuOpen);
    }
  };
  useEffect(() => {
    const buttonArea = document.querySelector('.content-generator__action-button');
    if (
      draftOptionsOpen &&
      buttonArea &&
      !buttonArea.classList.contains('content-generator__action-button--draft-options-open')
    ) {
      buttonArea.classList.add('content-generator__action-button--draft-options-open');
    } else if (!draftOptionsOpen && buttonArea) {
      buttonArea.classList.remove('content-generator__action-button--draft-options-open');
    }
  }, [draftOptionsOpen]);

  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      // Check if click is outside the menu.
      if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
        closeMenu();
      }
    }

    // Only add listener when menu is open.
    if (menuOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }

    // Cleanup.
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [menuOpen]);

  // This data can't be saved back to the Conductor API so store it in local state.
  const [toneAndStyle, setToneAndStyle] = useState<string>('');
  const [termsToExclude, setTermsToExclude] = useState<string[]>([]);
  const [additionalInstructions, setAdditionalInstructions] = useState<string>('');

  const editorRef = useRef<TextEditorRef>(null);
  if (!draftId) {
    return null;
  }

  const {
    data: outlineData,
    isLoading: outlineIsLoading,
    error: outlineError,
    stream: outlineStream,
    cancel: outlineCancel,
  } = useStream(
    `/conductor/proxy/v3/content-outline/content-generation?draft_id=${draftId}&cache=false`,
  );
  const {
    data: draftData,
    isLoading: draftIsLoading,
    error: draftError,
    stream: draftStream,
    cancel: draftCancel,
  } = useStream(
    `/conductor/proxy/v3/draft-generation/content-generation?draft_id=${draftId}&cache=false`,
  );

  // Menu buttons.
  const generateOutline = () => {
    editorRef.current?.clear();
    closeMenu();
    outlineStream({
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: '{}',
    });
  };
  useEffect(() => {
    if (outlineData.length) {
      const htmlOutlineData = marked(outlineData) as string;
      editorRef.current?.setText(htmlOutlineData);
    }
  }, [outlineData]);

  const generateDraft = () => {
    editorRef.current?.clear();
    closeMenu();
    draftStream({
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        voice_and_tone: toneAndStyle,
        terms_to_exclude: termsToExclude,
        additional_instructions: additionalInstructions,
      }),
    });
  };
  useEffect(() => {
    if (draftData.length) {
      const htmlDraftData = marked(draftData) as string;
      editorRef.current?.setText(htmlDraftData);
    }
  }, [draftData]);

  const cancelStreams = () => {
    outlineCancel();
    draftCancel();
  };

  // Store the pending text update
  const pendingTextRef = useRef<string | null>(null);
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
  const handleEditorChange = (newText: string) => {
    // Always store the latest text
    pendingTextRef.current = newText;

    // Clear any existing timeout
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }

    // Set a delay to apply the text
    timeoutRef.current = setTimeout(() => {
      // Only apply if not loading
      if (!draftIsLoading && !outlineIsLoading && pendingTextRef.current !== null) {
        setText(pendingTextRef.current);
        pendingTextRef.current = null;
      }
    }, 300);
  };

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []);
  // Apply pending changes when loading states change
  useEffect(() => {
    if (!draftIsLoading && !outlineIsLoading && pendingTextRef.current !== null) {
      setText(pendingTextRef.current);
      pendingTextRef.current = null;
    }
  }, [draftIsLoading, outlineIsLoading]);

  const streamingOverlay = (
    <div className="content-generator__streaming-overlay">
      <div className="content-generator__streaming-overlay-inner">
        <Sparkles />
        <p>Generating content</p>
        <p className="content-generator__message">
          <Info />
          <strong>Reminder:</strong> Writing Assistant generates content using AI technology, which
          can make mistakes. Be sure to fact-check important information before taking further
          action.
        </p>
        <Button variant="surface" size="1" onClick={cancelStreams}>
          Cancel
        </Button>
      </div>
    </div>
  );

  const errorOverlay = (
    <div className="content-generator__streaming-overlay">
      <div className="content-generator__streaming-overlay-inner">
        <p>Error</p>
        <p className="content-generator__message content-generator__message--error">
          <Bug />
          <strong>Error:</strong> There was an error generating content, please try again.
        </p>
        <Button variant="surface" size="1" onClick={outlineCancel}>
          Close
        </Button>
      </div>
    </div>
  );

  const floatingMenu = (
    <div className="content-generator__action-button" ref={menuRef}>
      <Lightbulb aria-label="Generate content with AI" role="button" onClick={toggleMenu} />
      {menuOpen && (
        <ul
          className="content-generator__context-menu"
          data-testid="content-generator__context-menu"
        >
          <li onClick={generateOutline}>
            <Menu />
            Replace with generated outline
          </li>
          <li onClick={openDraftOptions}>
            <Pen />
            Replace with generated draft
          </li>
        </ul>
      )}
      {draftOptionsOpen && (
        <div className="content-generator__context-menu content-generator__context-menu--draft-options">
          <FormLabel htmlFor="tone-and-style">Tone and style</FormLabel>
          <textarea
            data-testid="content-generator__tone-and-style"
            className="content-generator__tone-and-style"
            onChange={(e) => setToneAndStyle(e.target.value)}
            value={toneAndStyle}
            name="tone-and-style"
            aria-label="Tone and style"
            rows={2}
            placeholder="Describe the voice of your content"
          />
          <FormLabel>Terms to exclude</FormLabel>
          <TagInput tags={termsToExclude} setTags={(tags) => setTermsToExclude(tags)} />
          <FormLabel>Additional instructions</FormLabel>
          <textarea
            data-testid="content-generator__tone-and-style"
            className="content-generator__tone-and-style"
            onChange={(e) => setAdditionalInstructions(e.target.value)}
            value={additionalInstructions}
            name="tone-and-style"
            aria-label="Tone and style"
            rows={2}
            placeholder="What else should we know about your content?"
          />
          <div className="content-generator__buttons">
            <Button variant="surface" size="1" onClick={closeMenu}>
              Cancel
            </Button>
            <Button size="1" onClick={generateDraft}>
              Generate content
            </Button>
          </div>
        </div>
      )}
    </div>
  );

  return (
    <div
      className={`content-generator ${outlineIsLoading && !outlineError ? 'content-generator--is-streaming' : ''}`}
    >
      <FormLabel id="content-generator-label">Content</FormLabel>
      <TextEditor
        ref={editorRef}
        labelId="content-generator-label"
        initialText={initialText}
        onChange={handleEditorChange}
        floatingMenu={floatingMenu}
        overlay={
          ((outlineError || draftError) && errorOverlay) ||
          ((outlineIsLoading || draftIsLoading) && streamingOverlay) ||
          null
        }
      />
      <Button
        variant="surface"
        onClick={() => {
          const htmlText = editorRef.current?.getText() || '';
          const doc = new DOMParser().parseFromString(htmlText, 'text/html');
          navigator.clipboard.write([
            new ClipboardItem({
              'text/html': new Blob([htmlText], { type: 'text/html' }),
              'text/plain': new Blob([doc.body.textContent || ''], { type: 'text/plain' }),
            }),
          ]);
        }}
      >
        <Copy />
        Copy Text
      </Button>
    </div>
  );
}
