import React, { useRef, useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
import TextEditor, { type EditorHandle } 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 './ContentGeneratorTextArea.css';
import Button from './Button.js';
import TagInput from './TagInput.js';

export default function ContentGeneratorTextArea({
  draftId,
  initialText = '',
  setText = (_) => {},
}: {
  draftId: string | undefined;
  initialText?: string;
  setText?: (text: string) => void;
}) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [portalContainer, setPortalContainer] = useState<HTMLDivElement | null>(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-text-area__action-button');
    if (
      draftOptionsOpen &&
      buttonArea &&
      !buttonArea.classList.contains(
        'content-generator-text-area__action-button--draft-options-open',
      )
    ) {
      buttonArea.classList.add('content-generator-text-area__action-button--draft-options-open');
    } else if (!draftOptionsOpen && buttonArea) {
      buttonArea.classList.remove('content-generator-text-area__action-button--draft-options-open');
    }
  }, [draftOptionsOpen]);

  // 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 [editorReady, setEditorReady] = useState(false);
  const editorRef = useRef<EditorHandle>(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`,
  );

  useEffect(() => {
    if (editorRef.current) {
      setEditorReady(true);
    }
  }, [editorRef]);
  // Insert the menu button between the CKEditor toolbar and main text editing area.
  useEffect(() => {
    if (containerRef.current) {
      // This is for the production build.
      const existingToolbar = containerRef.current.querySelector('.ck-editor__top');
      if (existingToolbar && !portalContainer) {
        const customDiv = document.createElement('div');
        customDiv.className = 'content-generator-text-area__action-button';
        existingToolbar.insertAdjacentElement('afterend', customDiv);
        setPortalContainer(customDiv);
        return;
      }

      // This is for dev mode.
      // @todo Consolidate this/figure out why it needs to be done 2 different ways.
      const observer = new MutationObserver(() => {
        const button = containerRef.current?.querySelector(
          '.content-generator-text-area__action-button',
        );
        if (button) {
          observer.disconnect();
        }
        const editorToolbar = containerRef.current?.querySelector('.ck-editor__top');
        if (editorToolbar) {
          const customDiv = document.createElement('div');
          customDiv.className = 'content-generator-text-area__action-button';
          editorToolbar.insertAdjacentElement('afterend', customDiv);

          setPortalContainer(customDiv);
        }
      });
      observer.observe(containerRef.current, {
        childList: true,
        subtree: true,
      });
    }
  }, [editorReady]);

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

  const generateDraft = () => {
    editorRef.current?.setText('');
    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) {
      editorRef.current?.setText(draftData);
    }
  }, [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-text-area__streaming-overlay">
      <div className="content-generator-text-area__streaming-overlay-inner">
        <Sparkles />
        <p>Generating content</p>
        <p className="content-generator-text-area__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-text-area__streaming-overlay">
      <div className="content-generator-text-area__streaming-overlay-inner">
        <p>Error</p>
        <p className="content-generator-text-area__message content-generator-text-area__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>
  );

  return (
    <div
      className={`content-generator-text-area ${outlineIsLoading && !outlineError ? 'content-generator-text-area--is-streaming' : ''}`}
      ref={containerRef}
    >
      <FormLabel id="content-generator-text-area-label">Content</FormLabel>
      <TextEditor
        ref={editorRef}
        labelId="content-generator-text-area-label"
        initialText={initialText}
        onChange={handleEditorChange}
        overlay={
          ((outlineError || draftError) && errorOverlay) ||
          ((outlineIsLoading || draftIsLoading) && streamingOverlay) ||
          null
        }
      />
      {portalContainer &&
        createPortal(
          <>
            <Lightbulb aria-label="Generate content with AI" role="button" onClick={toggleMenu} />
            {menuOpen && (
              <ul
                className="content-generator-text-area__context-menu"
                data-testid="content-generator-text-area__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-text-area__context-menu content-generator-text-area__context-menu--draft-options">
                <FormLabel htmlFor="tone-and-style">Tone and style</FormLabel>
                <textarea
                  data-testid="content-generator-text-area__tone-and-style"
                  className="content-generator-text-area__tone-and-stype"
                  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-text-area__tone-and-style"
                  className="content-generator-text-area__tone-and-stype"
                  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-text-area__buttons">
                  <Button variant="surface" size="1" onClick={closeMenu}>
                    Cancel
                  </Button>
                  <Button size="1" onClick={generateDraft}>
                    Generate content
                  </Button>
                </div>
              </div>
            )}
          </>,
          portalContainer,
        )}
    </div>
  );
}
