<!doctype html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <%- include('partials/favicon') %>
    <title>채팅 - NCue</title>
    <link rel="stylesheet" href="/public/styles.css" />
  </head>
  <body>
    <div class="app-shell">
      <%- include('partials/nav', { activeMenu: (typeof activeMenu !== 'undefined' ? activeMenu : 'ai-explore') }) %>
      <div class="content-area chat-area">
        <header class="topbar">
          <h1>채팅</h1>
        </header>
        <main
          id="chatMain"
          class="chat-main"
          data-chat-gpt-allowed="<%= chatGptAllowed ? '1' : '0' %>"
          data-ops-state="<%= (typeof opsState !== 'undefined' ? String(opsState) : 'DEV') %>"
          data-admin-mode="<%= adminMode ? '1' : '0' %>"
          data-ops-user-email="<%= (typeof opsUserEmail !== 'undefined' && opsUserEmail) ? '1' : '0' %>"
        >
          <p id="chatApiWarning" class="chat-api-warning" hidden></p>
          <p id="chatGateNotice" class="chat-api-warning" hidden></p>
          <div class="chat-messages" id="chatMessages">
            <div class="chat-welcome" id="chatWelcome">
              <h2>안녕하세요, 오늘 무엇을 도와드릴까요?</h2>
              <p>사내 AI 챗봇으로 답변합니다.</p>
            </div>
          </div>
          <div class="chat-input-wrap">
            <form class="chat-form" id="chatForm">
              <textarea id="chatInput" placeholder="무엇이든 물어보세요" rows="1"></textarea>
              <button type="submit" id="sendBtn" class="chat-send-btn" title="전송">↑</button>
            </form>
            <p class="chat-disclaimer">AI 답변은 실수할 수 있습니다. 중요한 정보는 원문 규정과 함께 확인하세요.</p>
          </div>
        </main>
      </div>
    </div>

    <script src="/vendor/marked/marked.umd.js"></script>
    <script src="/vendor/dompurify/purify.min.js"></script>
    <script>
      (function () {
        const chatMain = document.getElementById('chatMain');
        var chatGptAllowed = !!(chatMain && chatMain.dataset.chatGptAllowed === '1');
        var opsState = (chatMain && chatMain.dataset.opsState) || 'DEV';
        var adminMode = !!(chatMain && chatMain.dataset.adminMode === '1');
        var opsUserEmail = !!(chatMain && chatMain.dataset.opsUserEmail === '1');
        const messagesEl = document.getElementById('chatMessages');
        const welcomeEl = document.getElementById('chatWelcome');
        const form = document.getElementById('chatForm');
        const input = document.getElementById('chatInput');
        const sendBtn = document.getElementById('sendBtn');
        let conversationHistory = [];

        function applyChatGptGate(allowed) {
          chatGptAllowed = !!allowed;
          var notice = document.getElementById('chatGateNotice');
          if (chatGptAllowed) {
            if (notice) notice.hidden = true;
            return;
          }
          if (input) input.disabled = true;
          if (sendBtn) sendBtn.disabled = true;
          if (notice) {
            notice.hidden = false;
            if (opsState === 'DEV' && !adminMode) {
              notice.textContent = '로그인 후 이용 가능합니다.';
            } else if (opsState === 'PROD' && !opsUserEmail) {
              notice.textContent = '회사 이메일 인증(로그인) 후 이용할 수 있습니다.';
            } else {
              notice.textContent = '이 환경에서는 GPT 채팅을 사용할 수 없습니다.';
            }
          }
        }

        function escapeHtml(s) {
          const div = document.createElement('div');
          div.textContent = s;
          return div.innerHTML;
        }

        var chatMarkdownConfigured = false;

        function configureChatMarkdown() {
          if (chatMarkdownConfigured) return;
          chatMarkdownConfigured = true;
          if (typeof marked !== 'undefined') {
            marked.setOptions({ breaks: true, gfm: true });
          }
          if (typeof DOMPurify !== 'undefined') {
            DOMPurify.addHook('afterSanitizeAttributes', function(node) {
              if (node.tagName === 'A' && node.hasAttribute('href')) {
                var href = node.getAttribute('href');
                try {
                  var u = new URL(href, window.location.href);
                  if (u.protocol !== 'http:' && u.protocol !== 'https:') {
                    node.removeAttribute('href');
                    return;
                  }
                } catch (e) {
                  node.removeAttribute('href');
                  return;
                }
                node.setAttribute('target', '_blank');
                node.setAttribute('rel', 'noopener noreferrer');
              }
            });
          }
        }

        function renderAssistantMarkdown(text) {
          configureChatMarkdown();
          if (typeof marked === 'undefined' || typeof DOMPurify === 'undefined') {
            return escapeHtml(text).replace(/\n/g, '<br>');
          }
          try {
            var raw = marked.parse(String(text || ''), { async: false });
            return DOMPurify.sanitize(raw, { USE_PROFILES: { html: true } });
          } catch (err) {
            return escapeHtml(text).replace(/\n/g, '<br>');
          }
        }

        function addMessage(role, content) {
          if (welcomeEl) welcomeEl.style.display = 'none';
          const div = document.createElement('div');
          div.className = 'chat-msg ' + (role === 'user' ? 'chat-msg-user' : 'chat-msg-assistant');
          var inner =
            role === 'assistant'
              ? renderAssistantMarkdown(content)
              : escapeHtml(content).replace(/\n/g, '<br>');
          var contentClass =
            role === 'assistant' ? 'chat-msg-content chat-md-body' : 'chat-msg-content';
          div.innerHTML = '<div class="' + contentClass + '">' + inner + '</div>';
          messagesEl.appendChild(div);
          messagesEl.scrollTop = messagesEl.scrollHeight;
        }

        function appendAssistantStreamingPlaceholder() {
          if (welcomeEl) welcomeEl.style.display = 'none';
          const div = document.createElement('div');
          div.className = 'chat-msg chat-msg-assistant chat-msg-streaming';
          const inner = document.createElement('div');
          inner.className = 'chat-msg-content chat-md-body';
          inner.innerHTML =
            '<span class="chat-progress-indicator" role="status" aria-live="polite" aria-label="응답 중">' +
            '<span class="chat-progress-text">응답 중</span>' +
            '<span class="chat-progress-dots" aria-hidden="true"><span></span><span></span><span></span></span>' +
            '<span class="chat-progress-track" aria-hidden="true"><span class="chat-progress-fill"></span></span>' +
            '</span>';
          div.appendChild(inner);
          const statusEl = document.createElement('div');
          statusEl.className = 'chat-status-line';
          statusEl.hidden = true;
          statusEl.setAttribute('aria-live', 'polite');
          div.appendChild(statusEl);
          const sourcesEl = document.createElement('div');
          sourcesEl.className = 'chat-sources';
          sourcesEl.hidden = true;
          div.appendChild(sourcesEl);
          messagesEl.appendChild(div);
          messagesEl.scrollTop = messagesEl.scrollHeight;
          return { bubble: div, contentEl: inner, statusEl: statusEl, sourcesEl: sourcesEl };
        }

        function appendSourceLinks(sourcesEl, items) {
          if (!sourcesEl || !items || !items.length) return;
          sourcesEl.innerHTML = '';
          const title = document.createElement('div');
          title.className = 'chat-sources-title';
          title.textContent = '출처';
          sourcesEl.appendChild(title);
          const ol = document.createElement('ol');
          ol.className = 'chat-sources-list';
          for (let i = 0; i < items.length; i++) {
            const item = items[i];
            const li = document.createElement('li');
            const t = item.title && String(item.title).trim();
            const ex = item.excerpt && String(item.excerpt).trim();
            const url = item && typeof item.url === 'string' ? item.url.trim() : '';
            let href = '';
            if (url) {
              try {
                const u = new URL(url);
                if (u.protocol === 'http:' || u.protocol === 'https:') {
                  href = u.href;
                }
              } catch (e) {}
            }
            if (href) {
              const a = document.createElement('a');
              a.href = href;
              a.target = '_blank';
              a.rel = 'noopener noreferrer';
              a.textContent = t || href;
              li.appendChild(a);
            } else {
              li.textContent = t || '출처';
            }
            if (ex) {
              const exDiv = document.createElement('div');
              exDiv.className = 'chat-source-excerpt';
              exDiv.textContent = ex;
              li.appendChild(exDiv);
            }
            ol.appendChild(li);
          }
          if (ol.children.length) {
            sourcesEl.appendChild(ol);
            sourcesEl.hidden = false;
          }
        }

        function setStreamingContent(contentEl, text) {
          contentEl.innerHTML = renderAssistantMarkdown(text);
          messagesEl.scrollTop = messagesEl.scrollHeight;
        }

        function extractApiErrorMessage(payload, fallback) {
          if (!payload) return fallback || '요청 실패';
          if (typeof payload === 'string') return payload;
          if (typeof payload.error === 'string' && payload.error.trim()) return payload.error.trim();
          if (payload.error && typeof payload.error === 'object') {
            if (typeof payload.error.message === 'string' && payload.error.message.trim()) {
              return payload.error.message.trim();
            }
            if (typeof payload.error.error === 'string' && payload.error.error.trim()) {
              return payload.error.error.trim();
            }
          }
          if (typeof payload.message === 'string' && payload.message.trim()) return payload.message.trim();
          return fallback || '요청 실패';
        }

        function setLoading(loading) {
          sendBtn.disabled = loading;
          sendBtn.textContent = loading ? '↻' : '↑';
          sendBtn.classList.toggle('is-busy', loading);
          sendBtn.setAttribute('aria-busy', loading ? 'true' : 'false');
          if (input) input.disabled = loading;
        }

        async function readSseStream(response, body) {
          const reader = response.body.getReader();
          const decoder = new TextDecoder();
          let buffer = '';
          let fullText = '';
          let started = false;
          while (true) {
            const { value, done } = await reader.read();
            if (done) break;
            buffer += decoder.decode(value, { stream: true });
            let sep;
            while ((sep = buffer.indexOf('\n\n')) !== -1) {
              const block = buffer.slice(0, sep);
              buffer = buffer.slice(sep + 2);
              const lines = block.split('\n');
              for (let i = 0; i < lines.length; i++) {
                const line = lines[i];
                if (line.indexOf('data: ') !== 0) continue;
                let obj;
                try {
                  obj = JSON.parse(line.slice(6));
                } catch (parseErr) {
                  continue;
                }
                if (obj.type === 'error') {
                  throw new Error(obj.error || '스트림 오류');
                }
                if (obj.type === 'status' && obj.phase === 'web_search' && body.statusEl) {
                  body.statusEl.textContent = '웹 검색 중...';
                  body.statusEl.hidden = false;
                  messagesEl.scrollTop = messagesEl.scrollHeight;
                }
                if (obj.type === 'sources' && body.sourcesEl && obj.items) {
                  appendSourceLinks(body.sourcesEl, obj.items);
                  messagesEl.scrollTop = messagesEl.scrollHeight;
                }
                if (obj.type === 'done' && body.statusEl) {
                  body.statusEl.hidden = true;
                }
                if (obj.type === 'delta' && obj.text) {
                  if (body.statusEl) body.statusEl.hidden = true;
                  if (!started) {
                    started = true;
                    body.bubble.classList.remove('chat-msg-streaming');
                  }
                  fullText += obj.text;
                  setStreamingContent(body.contentEl, fullText);
                }
              }
            }
          }
          return fullText;
        }

        applyChatGptGate(chatGptAllowed);

        fetch('/api/chat/config')
          .then(function(r) { return r.json(); })
          .then(function(cfg) {
            var w = document.getElementById('chatApiWarning');
            if (!cfg || !cfg.configured) {
              if (w) {
                w.hidden = false;
                w.innerHTML = '<strong>OpenAI API 연결 없음.</strong> 서버의 <code>OPENAI_API_KEY</code> 설정을 확인해 주세요.';
              }
              return;
            }
            if (cfg.error && w) {
              w.hidden = false;
              w.innerHTML = '<strong>채팅 오류.</strong> ' + escapeHtml(cfg.error);
            } else if (w) {
              w.hidden = true;
            }
            if (cfg && typeof cfg.chatGptAllowed === 'boolean') {
              applyChatGptGate(cfg.chatGptAllowed);
            }
          })
          .catch(function() {});

        form.addEventListener('submit', async function(e) {
          e.preventDefault();
          if (!chatGptAllowed) return;
          const text = (input.value || '').trim();
          if (!text) return;

          addMessage('user', text);
          conversationHistory.push({ role: 'user', content: text });
          input.value = '';
          input.style.height = 'auto';

          const body = appendAssistantStreamingPlaceholder();
          setLoading(true);

          try {
            const res = await fetch('/api/chat/stream', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify({ messages: conversationHistory.slice(-20) })
            });
            if (!res.ok) {
              const errData = await res.json().catch(function() { return {}; });
              body.bubble.remove();
              addMessage('assistant', '오류: ' + extractApiErrorMessage(errData, res.statusText || '요청 실패'));
              return;
            }
            const fullReply = await readSseStream(res, body);
            body.bubble.classList.remove('chat-msg-streaming');
            if (!fullReply.trim()) {
              setStreamingContent(body.contentEl, '(응답이 비어 있습니다.)');
            }
            conversationHistory.push({ role: 'assistant', content: fullReply || '(빈 응답)' });
          } catch (err) {
            body.bubble.remove();
            addMessage('assistant', '오류: ' + (err.message || '네트워크 오류'));
          } finally {
            if (body && body.statusEl) body.statusEl.hidden = true;
            setLoading(false);
          }
        });

        input.addEventListener('input', function() {
          this.style.height = 'auto';
          this.style.height = Math.min(this.scrollHeight, 120) + 'px';
        });
        input.addEventListener('keydown', function(e) {
          if (e.key === 'Enter' && !e.shiftKey) {
            e.preventDefault();
            form.dispatchEvent(new Event('submit'));
          }
        });
      })();
    </script>
  </body>
</html>
