/**
 * chat.mjs — AI-Do v4.0 chat component
 * Manages the central chat view with streaming support, markdown rendering,
 * code block copy, voice/upload buttons, and glassmorphic welcome screen.
 */

import store from '../store.mjs';
import eventBus, { EVENTS } from '../event-bus.mjs';
import api from '../api.mjs';
import { $, $$, createElement, icon, escapeHtml, empty } from '../utils/dom.mjs';
import { relativeTime } from '../utils/format.mjs';
import { renderMarkdown } from '../utils/markdown.mjs';
import { createOrb } from './voice-orb.mjs';
import { createAvatar } from './avatar.mjs';

// ── DOM references ──
let container = null;
let welcomeEl = null;
let messagesEl = null;
let inputArea = null;
let textarea = null;
let sendBtn = null;
let fileInput = null;
let streamingBubble = null;
let chatHeader = null;

// ── Quick action cards definition ──
const QUICK_ACTIONS = [
  {
    id: 'ask',
    icon: 'message-circle',
    title: 'Ask a Question',
    desc: 'Get answers and explanations on any topic',
    prompt: 'Can you help me understand '
  },
  {
    id: 'create',
    icon: 'plus-circle',
    title: 'Create Something',
    desc: 'Build pages, documents, or code files',
    prompt: 'Create a '
  },
  {
    id: 'analyze',
    icon: 'bar-chart-2',
    title: 'Analyze Data',
    desc: 'Examine, interpret, and summarize data',
    prompt: 'Analyze '
  },
  {
    id: 'research',
    icon: 'search',
    title: 'Learn & Research',
    desc: 'Explore topics and gather knowledge',
    prompt: 'Research '
  }
];

// ──────────────────────────────────────
// Public init
// ──────────────────────────────────────

export function init() {
  container = $('#view-chat');
  if (!container) {
    console.warn('[chat] #view-chat not found');
    return;
  }

  buildDOM();
  subscribeToStore();
  setupInputListeners();

  // Render existing messages from store
  const { messages } = store.getState().chat;
  if (messages.length > 0) {
    hideWelcome();
    renderAllMessages(messages);
  }
}

// ──────────────────────────────────────
// DOM construction
// ──────────────────────────────────────

function buildDOM() {
  empty(container);
  container.classList.add('chat-container');

  // Welcome screen
  welcomeEl = buildWelcomeScreen();
  container.appendChild(welcomeEl);

  // Chat header (shown when messages are visible)
  chatHeader = createElement('div', { className: 'chat-header' });
  chatHeader.style.display = 'none';

  const backBtn = createElement('button', {
    className: 'glass-button-icon chat-back-btn',
    'aria-label': 'New conversation'
  });
  backBtn.appendChild(icon('arrow-left', { size: 18 }));
  backBtn.appendChild(createElement('span', {}, ['New Chat']));
  backBtn.addEventListener('click', () => showWelcome());

  chatHeader.appendChild(backBtn);
  container.appendChild(chatHeader);

  // Messages area
  messagesEl = createElement('div', { className: 'chat-messages' });
  messagesEl.style.display = 'none';
  container.appendChild(messagesEl);

  // Connection status banner
  const connBanner = createElement('div', { className: 'chat-connection-banner' });
  connBanner.style.display = 'none';
  connBanner.appendChild(icon('warning', { size: 16 }));
  connBanner.appendChild(createElement('span', {}, ['Connection lost. Reconnecting...']));
  container.appendChild(connBanner);

  // Input area
  inputArea = buildInputArea();
  container.appendChild(inputArea);
}

function buildWelcomeScreen() {
  const persona = store.getState().persona;

  // Avatar above the voice orb
  const avatarEl = createAvatar('welcome');

  // Voice orb (full size) for welcome screen
  const orbEl = createOrb('full');

  const greeting = createElement('div', { className: 'chat-welcome-greeting' }, [
    avatarEl,
    orbEl,
    createElement('h2', {}, [persona.name || 'AI-Do']),
    createElement('p', {}, [persona.greeting || 'Hello! How can I help you today?'])
  ]);

  const cardsGrid = createElement('div', { className: 'chat-quick-actions' });

  for (const action of QUICK_ACTIONS) {
    const card = createElement('div', { className: 'glass-card', dataset: { action: action.id } });

    const cardIcon = createElement('div', { className: 'card-icon' });
    cardIcon.appendChild(icon(action.icon, { size: 18 }));

    const cardTitle = createElement('div', { className: 'card-title' }, [action.title]);
    const cardDesc = createElement('div', { className: 'card-desc' }, [action.desc]);

    card.appendChild(cardIcon);
    card.appendChild(cardTitle);
    card.appendChild(cardDesc);

    card.addEventListener('click', () => {
      if (textarea) {
        textarea.value = action.prompt;
        textarea.focus();
        autoResizeTextarea();
        updateSendButton();
      }
    });

    cardsGrid.appendChild(card);
  }

  const el = createElement('div', { className: 'chat-welcome' });
  el.appendChild(greeting);
  el.appendChild(cardsGrid);
  return el;
}

function buildInputArea() {
  const area = createElement('div', { className: 'chat-input-area' });

  // Upload button
  const uploadBtn = createElement('button', {
    className: 'btn-upload',
    'aria-label': 'Upload file'
  });
  uploadBtn.appendChild(icon('upload', { size: 18 }));

  fileInput = createElement('input', {
    className: 'file-input-hidden',
    type: 'file',
    'aria-hidden': 'true'
  });

  uploadBtn.addEventListener('click', () => fileInput.click());
  fileInput.addEventListener('change', handleFileUpload);

  // Compact voice orb (replaces placeholder mic button)
  const micBtn = createOrb('compact');

  // Textarea
  textarea = createElement('textarea', {
    className: 'glass-textarea',
    placeholder: 'Type a message...',
    rows: '1',
    'aria-label': 'Chat message input'
  });

  // Send button
  sendBtn = createElement('button', {
    className: 'btn-send',
    'aria-label': 'Send message',
    disabled: 'true'
  });
  sendBtn.appendChild(icon('send', { size: 18 }));

  area.appendChild(uploadBtn);
  area.appendChild(fileInput);
  area.appendChild(micBtn);
  area.appendChild(textarea);
  area.appendChild(sendBtn);

  return area;
}

// ──────────────────────────────────────
// Store subscriptions
// ──────────────────────────────────────

function subscribeToStore() {
  // Messages changed (full replacement, e.g. initial load)
  store.subscribe(
    (s) => s.chat.messages,
    (messages) => {
      if (messages.length > 0) {
        hideWelcome();
      }
      renderAllMessages(messages);
    }
  );

  // Streaming state
  store.subscribe(
    (s) => s.chat.streaming,
    (streaming) => {
      if (streaming) {
        hideWelcome();
        showTypingIndicator();
        swapToStopButton();
      } else {
        removeTypingIndicator();
        finalizeStreamingBubble();
        swapToSendButton();
      }
      // Announce streaming state to screen readers
      const srEl = document.getElementById('sr-announcements');
      if (srEl) srEl.textContent = streaming ? 'AI is responding...' : 'AI response complete';
    }
  );

  // Stream text accumulation
  store.subscribe(
    (s) => s.chat.streamText,
    (text) => {
      if (text && store.getState().chat.streaming) {
        updateStreamingBubble(text);
      }
    }
  );

  // Persona update (refresh welcome greeting)
  store.subscribe(
    (s) => s.persona.greeting,
    () => {
      if (welcomeEl && welcomeEl.style.display !== 'none') {
        const persona = store.getState().persona;
        const h2 = $('h2', welcomeEl);
        const p = $('p', welcomeEl);
        if (h2) h2.textContent = persona.name || 'AI-Do';
        if (p) p.textContent = persona.greeting || 'Hello! How can I help you today?';
      }
    }
  );

  // New chat message from WebSocket (agent replies)
  eventBus.on(EVENTS.CHAT_MESSAGE, (data) => {
    // Already dispatched to store by api.mjs _handleWsMessage
    // We scroll to bottom after the store subscriber renders it
    requestAnimationFrame(() => scrollToBottom());
  });

  // Stream end — finalize the streaming bubble with the real message
  eventBus.on(EVENTS.CHAT_STREAM_END, (data) => {
    if (data?.text) {
      finalizeStreamingBubbleWithContent(data.text, data.id);
    }
  });

  // Connection status banner
  eventBus.on(EVENTS.WS_DISCONNECTED, () => {
    const banner = container?.querySelector('.chat-connection-banner');
    if (banner) banner.style.display = 'flex';
  });

  eventBus.on(EVENTS.WS_CONNECTED, () => {
    const banner = container?.querySelector('.chat-connection-banner');
    if (banner) banner.style.display = 'none';
  });
}

// ──────────────────────────────────────
// Input handling
// ──────────────────────────────────────

function setupInputListeners() {
  textarea.addEventListener('input', () => {
    autoResizeTextarea();
    updateSendButton();
  });

  textarea.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      if (store.getState().chat.streaming) return;
      const text = textarea.value.trim();
      if (text) sendMessage(text);
    }
  });

  sendBtn.addEventListener('click', () => {
    if (store.getState().chat.streaming) {
      api.cancelStream();
      return;
    }
    const text = textarea.value.trim();
    if (text) sendMessage(text);
  });
}

function autoResizeTextarea() {
  textarea.style.height = 'auto';
  const lineHeight = parseInt(getComputedStyle(textarea).lineHeight, 10) || 24;
  const maxRows = 6;
  const maxHeight = lineHeight * maxRows;
  textarea.style.height = Math.min(textarea.scrollHeight, maxHeight) + 'px';
}

function updateSendButton() {
  const hasText = textarea.value.trim().length > 0;
  sendBtn.disabled = !hasText;
}

async function sendMessage(text) {
  // Add user message to store
  store.dispatch('ADD_MESSAGE', {
    role: 'user',
    content: text,
    timestamp: Date.now()
  });

  // Clear input
  textarea.value = '';
  autoResizeTextarea();
  updateSendButton();

  // Hide welcome, show messages
  hideWelcome();

  // Scroll to bottom after render
  requestAnimationFrame(() => scrollToBottom());

  // Send via streaming API
  try {
    await api.streamMessage(text);
  } catch (err) {
    console.error('[chat] Send error:', err);
  }
}

function handleFileUpload() {
  if (!fileInput.files || fileInput.files.length === 0) return;

  const file = fileInput.files[0];
  const formData = new FormData();
  formData.append('file', file);

  api.post('/api/ingest', formData)
    .then(() => {
      store.dispatch('ADD_MESSAGE', {
        role: 'user',
        content: `Uploaded file: ${file.name}`,
        timestamp: Date.now()
      });
      hideWelcome();
    })
    .catch((err) => {
      console.error('[chat] Upload error:', err);
    })
    .finally(() => {
      fileInput.value = '';
    });
}

// ──────────────────────────────────────
// Send / Stop button swap
// ──────────────────────────────────────

function swapToStopButton() {
  empty(sendBtn);
  sendBtn.className = 'btn-stop';
  sendBtn.disabled = false;
  sendBtn.setAttribute('aria-label', 'Stop generation');
  sendBtn.appendChild(icon('square', { size: 16 }));

  // Rebind click to cancel
  sendBtn.onclick = () => api.cancelStream();
}

function swapToSendButton() {
  empty(sendBtn);
  sendBtn.className = 'btn-send';
  sendBtn.setAttribute('aria-label', 'Send message');
  sendBtn.appendChild(icon('send', { size: 18 }));
  updateSendButton();

  // Rebind click to send
  sendBtn.onclick = () => {
    const text = textarea.value.trim();
    if (text && !store.getState().chat.streaming) sendMessage(text);
  };
}

// ──────────────────────────────────────
// Message rendering
// ──────────────────────────────────────

function renderAllMessages(messages) {
  if (!messagesEl) return;
  empty(messagesEl);
  for (const msg of messages) {
    const el = renderMessage(msg, false);
    messagesEl.appendChild(el);
  }
  scrollToBottom();
}

function renderMessage(msg, animate = true) {
  const isUser = msg.role === 'user';

  const row = createElement('div', {
    className: `chat-message ${isUser ? 'user' : 'agent'}${animate ? ' animate-in' : ''}`,
    dataset: { id: String(msg.id || '') }
  });

  // Avatar
  const avatar = createElement('div', { className: 'avatar' });
  if (isUser) {
    avatar.textContent = 'U';
  } else {
    avatar.textContent = 'A';
  }

  // Bubble
  const bubble = createElement('div', { className: 'bubble' });
  const content = createElement('div', { className: 'content' });

  if (isUser) {
    content.textContent = msg.content || '';
  } else {
    // Render markdown async, set innerHTML
    content.innerHTML = '<span style="color: var(--text-tertiary)">...</span>';
    renderMarkdown(msg.content || '').then((html) => {
      content.innerHTML = html;
      addCodeCopyButtons(content);
    });
  }

  // Timestamp
  const ts = createElement('div', { className: 'timestamp' });
  ts.textContent = msg.timestamp ? relativeTime(msg.timestamp) : '';

  bubble.appendChild(content);
  bubble.appendChild(ts);
  row.appendChild(avatar);
  row.appendChild(bubble);

  return row;
}

// ──────────────────────────────────────
// Streaming bubble
// ──────────────────────────────────────

function showTypingIndicator() {
  if (!messagesEl) return;

  // Create a streaming message row
  streamingBubble = createElement('div', {
    className: 'chat-message agent animate-in',
    dataset: { streaming: 'true' }
  });

  const avatar = createElement('div', { className: 'avatar' });
  avatar.textContent = 'A';

  const bubble = createElement('div', { className: 'bubble' });
  const content = createElement('div', { className: 'content' });

  // Typing indicator dots
  const typingEl = createElement('div', { className: 'chat-typing-indicator' }, [
    createElement('span'),
    createElement('span'),
    createElement('span')
  ]);
  content.appendChild(typingEl);

  bubble.appendChild(content);
  streamingBubble.appendChild(avatar);
  streamingBubble.appendChild(bubble);

  messagesEl.style.display = 'flex';
  messagesEl.appendChild(streamingBubble);

  // Add shimmer bar
  const shimmer = createElement('div', { className: 'chat-streaming' });
  shimmer.dataset.streamingBar = 'true';
  messagesEl.parentNode.insertBefore(shimmer, messagesEl.nextSibling);

  scrollToBottom();
}

function updateStreamingBubble(text) {
  if (!streamingBubble) return;

  const content = $('.content', streamingBubble);
  if (!content) return;

  // Replace typing indicator with rendered text
  renderMarkdown(text).then((html) => {
    content.innerHTML = html;
    addCodeCopyButtons(content);
    scrollToBottom();
  });
}

function removeTypingIndicator() {
  // Remove shimmer bar
  const shimmer = container?.querySelector('[data-streaming-bar]');
  if (shimmer) shimmer.remove();
}

function finalizeStreamingBubble() {
  if (!streamingBubble) return;

  // Remove the streaming attribute
  delete streamingBubble.dataset.streaming;
  streamingBubble.classList.remove('animate-in');

  // Add timestamp
  const bubble = $('.bubble', streamingBubble);
  if (bubble && !$('.timestamp', bubble)) {
    const ts = createElement('div', { className: 'timestamp' });
    ts.textContent = relativeTime(Date.now());
    bubble.appendChild(ts);
  }

  streamingBubble = null;
}

function finalizeStreamingBubbleWithContent(text, id) {
  if (!streamingBubble) return;

  const content = $('.content', streamingBubble);
  if (content) {
    renderMarkdown(text).then((html) => {
      content.innerHTML = html;
      addCodeCopyButtons(content);
    });
  }

  if (id) {
    streamingBubble.dataset.id = String(id);
  }

  finalizeStreamingBubble();
}

// ──────────────────────────────────────
// Collapsible code blocks with copy buttons
// ──────────────────────────────────────

function addCodeCopyButtons(parentEl) {
  const preBlocks = $$('pre', parentEl);
  for (const pre of preBlocks) {
    // Skip if already wrapped in a collapsible container
    if (pre.parentElement?.classList.contains('code-collapse-body')) continue;

    // Extract language from the code element's class (e.g. "language-javascript")
    const codeEl = $('code', pre);
    let lang = '';
    if (codeEl) {
      const langClass = [...codeEl.classList].find(c => c.startsWith('language-'));
      if (langClass) lang = langClass.replace('language-', '');
    }
    const langLabel = lang || 'code';

    // Build the collapsible wrapper using <details>
    const details = createElement('details', { className: 'code-collapse' });

    // Summary = clickable header row
    const summary = createElement('summary', { className: 'code-collapse-header' });

    // Language badge
    const langBadge = createElement('span', { className: 'code-collapse-lang' }, [langLabel]);

    // Toggle label text (updates on open/close)
    const toggleLabel = createElement('span', { className: 'code-collapse-toggle' }, ['Show code']);

    // Chevron icon
    const chevron = createElement('span', { className: 'code-collapse-chevron' });
    chevron.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>';

    // Copy button (works even when collapsed)
    const copyBtn = createElement('button', { className: 'code-collapse-copy' }, ['Copy']);
    copyBtn.addEventListener('click', (e) => {
      e.stopPropagation();
      e.preventDefault();
      const code = $('code', pre);
      const text = code ? code.textContent : pre.textContent;
      navigator.clipboard.writeText(text).then(() => {
        copyBtn.textContent = 'Copied!';
        copyBtn.classList.add('copied');
        setTimeout(() => {
          copyBtn.textContent = 'Copy';
          copyBtn.classList.remove('copied');
        }, 2000);
      }).catch(() => {
        copyBtn.textContent = 'Failed';
        setTimeout(() => { copyBtn.textContent = 'Copy'; }, 2000);
      });
    });

    summary.appendChild(langBadge);
    summary.appendChild(toggleLabel);
    summary.appendChild(copyBtn);
    summary.appendChild(chevron);

    // Body wrapper holds the pre block
    const body = createElement('div', { className: 'code-collapse-body' });

    // Insert the details element where the pre was
    pre.parentNode.insertBefore(details, pre);
    body.appendChild(pre);
    details.appendChild(summary);
    details.appendChild(body);

    // Update toggle label on open/close
    details.addEventListener('toggle', () => {
      toggleLabel.textContent = details.open ? 'Hide code' : 'Show code';
    });
  }
}

// ──────────────────────────────────────
// Helpers
// ──────────────────────────────────────

function hideWelcome() {
  if (welcomeEl) welcomeEl.style.display = 'none';
  if (messagesEl) messagesEl.style.display = 'flex';
  if (chatHeader) chatHeader.style.display = 'flex';
}

function showWelcome() {
  if (welcomeEl) welcomeEl.style.display = '';
  if (messagesEl) messagesEl.style.display = 'none';
  if (chatHeader) chatHeader.style.display = 'none';
  // Don't clear messages — they persist in store
}

function scrollToBottom() {
  if (!messagesEl) return;
  requestAnimationFrame(() => {
    messagesEl.scrollTop = messagesEl.scrollHeight;
  });
}
