/**
 * EditModal opens a modal for editing a message (text, reasoning, or
 * tool call). Reads from and writes to the conversation tree. Returns
 * a Promise so the App knows when to re-render.
 */

import { extractText } from './conversation.js';

export class EditModal {
  /**
   * @param {Object} options
   * @param {Document} options.document
   * @param {import('./conversation.js').ConversationTree} options.conversation
   */
  constructor({ document, conversation }) {
    this.document = document;
    this.conversation = conversation;
  }

  /**
   * open creates and shows the edit modal for a message.
   * @param {string} messageId
   * @param {string} [field] - Optional: 'reasoning' or 'tool'
   * @param {string} [toolCallId] - Optional, for tool edits
   * @returns {Promise<boolean>} true if saved, false if cancelled
   */
  open(messageId, field = null, toolCallId = null) {
    const msg = this.conversation.get(messageId);
    if (!msg) return Promise.resolve(false);

    return new Promise((resolve) => {
      const isToolEdit = field === 'tool';
      const tplId = isToolEdit ? 'tpl-edit-tool-modal' : 'tpl-edit-modal';
      const $tpl = this.document.getElementById(tplId);
      if (!$tpl) return resolve(false);

      const $modal = $tpl.content.cloneNode(true).firstElementChild;
      const $overlay = this.document.createElement('div');
      $overlay.className = 'modal-overlay';
      $overlay.appendChild($modal);

      if (!isToolEdit) {
        $modal.querySelector('h2').textContent =
          field === 'reasoning' ? 'Edit Reasoning' : 'Edit Message';
      }

      const saveBtn = $modal.querySelector('#edit-save');
      const closeBtn = $modal.querySelector('.modal__close');

      // Populate textarea(s).
      if (isToolEdit && toolCallId) {
        this._populateToolEdit(msg, toolCallId, $modal);
      } else if (field === 'reasoning') {
        const textarea = $modal.querySelector('#edit-textarea');
        textarea.value = msg.reasoning_content || '';
      } else {
        const textarea = $modal.querySelector('#edit-textarea');
        textarea.value = extractText(msg.content);
      }

      const close = () => {
        $overlay.remove();
        this.document.removeEventListener('keydown', handleEscape);
        resolve(false);
      };

      const save = () => {
        const saved = this._save(messageId, $modal, field, toolCallId, saveBtn);
        if (saved) {
          $overlay.remove();
          this.document.removeEventListener('keydown', handleEscape);
          resolve(true);
        }
      };

      closeBtn.addEventListener('click', close);
      saveBtn.addEventListener('click', save);

      $overlay.addEventListener('click', (e) => {
        if (e.target === $overlay) close();
      });

      const handleEscape = (e) => {
        if (e.key === 'Escape') {
          close();
        }
      };
      this.document.addEventListener('keydown', handleEscape);

      this.document.body.appendChild($overlay);

      const firstTextarea = $modal.querySelector('textarea');
      if (firstTextarea) firstTextarea.focus();
    });
  }

  // --- Private ---

  _populateToolEdit(msg, toolCallId, $modal) {
    const toolCall = msg.tool_calls?.find((tc) => tc.id === toolCallId);
    if (!toolCall) return;

    const $callTextarea = $modal.querySelector('#edit-tool-call');
    const $resultTextarea = $modal.querySelector('#edit-tool-result');

    try {
      const args = JSON.parse(toolCall.function?.arguments || '{}');
      $callTextarea.value = JSON.stringify(
        { name: toolCall.function?.name, arguments: args },
        null,
        2,
      );
    } catch {
      $callTextarea.value = JSON.stringify(
        {
          name: toolCall.function?.name,
          arguments: toolCall.function?.arguments || '',
        },
        null,
        2,
      );
    }

    for (const [, m] of this.conversation.messagesMap) {
      if (m.role === 'tool' && m.tool_call_id === toolCallId) {
        $resultTextarea.value = extractText(m.content) || '';
        break;
      }
    }
  }

  _save(messageId, $modal, field, toolCallId, saveBtn) {
    const msg = this.conversation.get(messageId);
    if (!msg) return false;

    if (field === 'tool' && toolCallId) {
      return this._saveToolEdit(msg, toolCallId, $modal, saveBtn);
    }

    if (field === 'reasoning') {
      const textarea = $modal.querySelector('#edit-textarea');
      msg.reasoning_content = textarea.value;
      this.conversation.save();
      return true;
    }

    // Regular message edit.
    const textarea = $modal.querySelector('#edit-textarea');
    const newText = textarea.value;
    if (Array.isArray(msg.content)) {
      const textPart = msg.content.find((p) => p.type === 'text');
      if (textPart) {
        textPart.text = newText;
      } else {
        msg.content.unshift({ type: 'text', text: newText });
      }
    } else {
      msg.content = newText;
    }
    this.conversation.save();
    return true;
  }

  _saveToolEdit(msg, toolCallId, $modal, saveBtn) {
    const toolCall = msg.tool_calls?.find((tc) => tc.id === toolCallId);
    if (!toolCall) return false;

    const $callTextarea = $modal.querySelector('#edit-tool-call');
    const $resultTextarea = $modal.querySelector('#edit-tool-result');

    let parsedCall;
    try {
      parsedCall = JSON.parse($callTextarea.value);
    } catch {
      $callTextarea.classList.add('modal__textarea--error');
      saveBtn.classList.add('modal__save--error');
      setTimeout(() => {
        saveBtn.classList.remove('modal__save--error');
      }, 500);
      return false;
    }

    if (parsedCall.name) {
      toolCall.function.name = parsedCall.name;
    }
    if (parsedCall.arguments !== undefined) {
      toolCall.function.arguments =
        typeof parsedCall.arguments === 'string'
          ? parsedCall.arguments
          : JSON.stringify(parsedCall.arguments);
    }

    for (const [, m] of this.conversation.messagesMap) {
      if (m.role === 'tool' && m.tool_call_id === toolCallId) {
        const newText = $resultTextarea.value;
        if (Array.isArray(m.content)) {
          const textPart = m.content.find((p) => p.type === 'text');
          if (textPart) {
            textPart.text = newText;
          } else {
            m.content.unshift({ type: 'text', text: newText });
          }
        } else {
          m.content = newText;
        }
        break;
      }
    }

    this.conversation.save();
    return true;
  }
}
