const output = document.getElementById('chat-output');
const input = document.getElementById('chat-input');
const btn = document.getElementById('send-btn');
const endpointInput = document.getElementById('endpoint-url');
const modelSelect = document.getElementById('model-list');

let history = [];

const AUX_LOCALAI_ENDPOINT = 'aux.localai.endpoint';
const AUX_LOCALAI_MODEL = 'aux.localai.model';
const AUX_LOCALAI_FEED = '/v1/models';
const AUX_LOCALAI_CHAT = '/v1/chat/completions';
const AUX_RECORDING_CONTRACT = 'VerbatimRecordingContract.txt';

let recordingContractText = '';
let recordingContractReady = false;

// ── LOCAL AI CONTRACT ──────────────────────────────────────────────────
async function loadRecordingContract() {
  try {
    const res = await fetch(chrome.runtime.getURL(AUX_RECORDING_CONTRACT));
    if (!res.ok) throw new Error('contract_fetch_failed');

    recordingContractText = await res.text();
    recordingContractReady = Boolean(recordingContractText.trim());
  } catch {
    recordingContractText = '';
    recordingContractReady = false;
  }

  registerLocalSurface();
  return recordingContractReady;
}

function recordingContractPrompt() {
  return [
    'Verbatim Recording Contract is already shared by AUX Local AI Skin.',
    'Use only the valid recorder actions, payload shapes, and constraints from this contract.',
    recordingContractText
  ].join('\n\n');
}

function localSystemPrompt() {
  const modelName = modelSelect.value || 'the selected local model';

  return [
    `You are ${modelName}, running inside Nodarama's AUX Local AI Skin.`,
    'The skin connects the selected local model to AUX and may provide returned Verbatim project context.',
    'Respect the AUX Process Contract: STATUS, SCAN_CHECK, VERBATIM_DISPATCH, TEMP_RENDER, LOCAL_AI, CONFIG_ROUTE.',
    'When returned Verbatim context is provided, use it as the current project source of truth.',
    'For recorder changes, produce recorder-ready manifests only with valid Verbatim actions.',
    'Do not invent files, routes, functions, or project state not present in the provided context.'
  ].join('\n');
}

async function readLocalAiConfig() {
  if (window.chrome && chrome.storage && chrome.storage.local) {
    const stored = await chrome.storage.local.get([AUX_LOCALAI_ENDPOINT, AUX_LOCALAI_MODEL]);

    if (stored[AUX_LOCALAI_ENDPOINT]) endpointInput.value = stored[AUX_LOCALAI_ENDPOINT];
    if (stored[AUX_LOCALAI_MODEL]) modelSelect.dataset.savedModel = stored[AUX_LOCALAI_MODEL];
  } else if (localStorage.getItem('nv_local_endpoint')) {
    endpointInput.value = localStorage.getItem('nv_local_endpoint');
  }
}

async function writeLocalAiConfig(base, model = '') {
  if (window.chrome && chrome.storage && chrome.storage.local) {
    await chrome.storage.local.set({
      [AUX_LOCALAI_ENDPOINT]: base,
      [AUX_LOCALAI_MODEL]: model || modelSelect.value || ''
    });
  }

  localStorage.setItem('nv_local_endpoint', base);
}

async function loadModels() {
  registerLocalSurface();

  const base = endpointInput.value.trim().replace(/\/+$/, '');
  await writeLocalAiConfig(base);

  try {
    const res = await fetch(`${base}${AUX_LOCALAI_FEED}`);
    const data = await res.json();

    modelSelect.innerHTML = '';

    if (data && Array.isArray(data.data) && data.data.length > 0) {
      data.data.forEach(m => {
        const opt = document.createElement('option');
        opt.value = m.id;
        opt.textContent = m.id;
        modelSelect.appendChild(opt);
      });

      if (modelSelect.dataset.savedModel) modelSelect.value = modelSelect.dataset.savedModel;
    } else {
      modelSelect.innerHTML = '<option value="default">Default Local Model</option>';
    }
  } catch {
    modelSelect.innerHTML = '<option value="">Local Engine Offline</option>';
  }

  registerLocalSurface();
}

endpointInput.addEventListener('change', loadModels);

const saveHostBtn = document.getElementById('save-host-btn');

if (saveHostBtn) {
  saveHostBtn.addEventListener('click', e => {
    e.preventDefault();
    loadModels();
  });
}

readLocalAiConfig()
  .then(loadRecordingContract)
  .then(loadModels);

// ── STATUS REGISTRATION ────────────────────────────────────────────────
function registerLocalSurface() {
  if (!(window.chrome && chrome.runtime)) return;

  chrome.runtime.sendMessage({
    type: 'AUX_REGISTER_SURFACE',
    provider: 'LOCAL',
    surfaceType: 'LOCAL',
    surfaceName: modelSelect.value || 'Local AI Skin',
    contractAware: recordingContractReady
  }, () => void chrome.runtime.lastError);
}

// ── LOCAL AI CHAT ───────────────────────────────────────────────────────
async function streamResponse() {
  const text = input.value.trim();
  if (!text) return;

  input.value = '';
  appendMessage('user', text);
  history.push({ role: 'user', content: text });

  const aiBubble = appendMessage('ai', 'Connecting...');
  aiBubble.textContent = '';

  const base = endpointInput.value.trim().replace(/\/+$/, '');

  try {
    const response = await fetch(`${base}${AUX_LOCALAI_CHAT}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        model: modelSelect.value || 'default',
        messages: [
          { role: 'system', content: localSystemPrompt() },
          ...(recordingContractReady ? [{ role: 'system', content: recordingContractPrompt() }] : []),
          ...history
        ],
        stream: true
      })
    });

    if (!response.ok) throw new Error();

    const reader = response.body.getReader();
    const decoder = new TextDecoder('utf-8');

    let fullText = '';
    let buffer = '';

    while (true) {
      const { value, done } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });

      const lines = buffer.split('\n');
      buffer = lines.pop();

      for (const line of lines) {
        const trimmed = line.trim();

        if (!trimmed || !trimmed.startsWith('data: ') || trimmed.includes('[DONE]')) continue;

        try {
          const parsed = JSON.parse(trimmed.slice(6));
          const content = parsed.choices[0].delta.content || '';

          fullText += content;
          aiBubble.textContent = fullText;
          output.scrollTop = output.scrollHeight;
        } catch {
          // Ignore partial JSON chunks.
        }
      }
    }

    history.push({ role: 'assistant', content: fullText });
    registerLocalSurface();
  } catch {
    aiBubble.textContent = 'Connection failed. Verify your local engine is running and the endpoint address matches.';
  }
}

function appendMessage(sender, msg) {
  const div = document.createElement('div');

  div.className = `msg ${sender}`;
  div.textContent = msg;

  output.appendChild(div);
  output.scrollTop = output.scrollHeight;

  return div;
}

// ── LOCAL AI FEED ───────────────────────────────────────────────────────
if (window.chrome && chrome.runtime) {
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message?.type !== 'AUX_LOCALAI_FEED_TEXT') return false;

    const text = String(message.text || '').trim();

    if (!text) {
      sendResponse({ ok: false, error: 'empty_feed' });
      return true;
    }

    input.value = text;
    registerLocalSurface();

    sendResponse({ ok: true });

    if (message.submit !== false) streamResponse();

    return true;
  });
}

btn.addEventListener('click', streamResponse);

input.addEventListener('keydown', e => {
  if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) {
    e.preventDefault();
    streamResponse();
  } else if (e.key === 'Enter' && e.ctrlKey) {
    input.value += '\n';
  }
});