/**
 * Flynn Settings Page
 *
 * Read-only config view (redacted), editable hook patterns,
 * tool list, and channel overview.
 */
import {
  isPushSupported,
  getPushStatus,
  enablePushNotifications,
  disablePushNotifications,
} from '../lib/pwa.js';

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

let _client = null;
let _el = null;
let _settingsCache = null;

function describePushStatus(status) {
  if (!status.supported) {
    return status.message || 'Push notifications are not supported in this browser.';
  }
  if (!status.enabled) {
    return 'Gateway push is disabled (`server.webchat_push.enabled: false`).';
  }
  if (!status.configured) {
    return 'Gateway push key is missing (`server.webchat_push.vapid_public_key`).';
  }
  if (status.permission === 'denied') {
    return 'Browser notifications are blocked. Allow notifications in browser settings.';
  }
  if (status.subscribed) {
    return 'Push notifications are enabled for this browser.';
  }
  return 'Push is configured. Click Enable to subscribe this browser.';
}

async function renderPushStatus() {
  const statusEl = _el.querySelector('#push-status');
  const enableBtn = _el.querySelector('#push-enable');
  const disableBtn = _el.querySelector('#push-disable');
  if (!statusEl || !enableBtn || !disableBtn) {
    return;
  }

  try {
    const status = await getPushStatus();
    statusEl.textContent = describePushStatus(status);
    statusEl.className = status.subscribed ? 'text-sm text-green-500' : 'text-sm text-zinc-500';
    enableBtn.disabled = !status.supported || !status.enabled || !status.configured || status.subscribed;
    disableBtn.disabled = !status.supported || !status.subscribed;
  } catch (err) {
    statusEl.textContent = `Push status error: ${err.message}`;
    statusEl.className = 'text-sm text-red-500';
    enableBtn.disabled = true;
    disableBtn.disabled = true;
  }
}

async function onEnablePush() {
  const statusEl = _el.querySelector('#push-status');
  if (!statusEl) {return;}
  statusEl.textContent = 'Enabling push notifications...';
  statusEl.className = 'text-sm text-zinc-500';
  try {
    await enablePushNotifications();
    await renderPushStatus();
  } catch (err) {
    statusEl.textContent = `Enable failed: ${err.message}`;
    statusEl.className = 'text-sm text-red-500';
  }
}

async function onDisablePush() {
  const statusEl = _el.querySelector('#push-status');
  if (!statusEl) {return;}
  statusEl.textContent = 'Disabling push notifications...';
  statusEl.className = 'text-sm text-zinc-500';
  try {
    await disablePushNotifications();
    await renderPushStatus();
  } catch (err) {
    statusEl.textContent = `Disable failed: ${err.message}`;
    statusEl.className = 'text-sm text-red-500';
  }
}

async function loadSettings() {
  if (!_client || !_el) {return;}

  let config, tools;
  let services;

  try {
    [config, tools, services] = await Promise.all([
      _client.call('config.get'),
      _client.call('tools.list'),
      _client.call('system.services'),
    ]);
  } catch (err) {
    _el.innerHTML = `
      <h1 class="text-2xl font-semibold text-zinc-50 mb-6">Settings</h1>
      <div class="text-center py-12 px-6 text-zinc-500 text-sm">Failed to load settings: ${err.message}</div>
    `;
    return;
  }

  // Extract hooks from config
  const hooks = config?.hooks ?? {};
  const confirmPatterns = hooks.confirm ?? [];
  const logPatterns = hooks.log ?? [];
  const silentPatterns = hooks.silent ?? [];
  const automation = config?.automation ?? {};
  const memory = config?.memory ?? {};
  const tts = config?.tts ?? {};
  _settingsCache = config ?? {};

  const deliveryMode = automation.delivery_mode ?? 'shared_session';
  const dailyBriefingEnabled = Boolean(automation.daily_briefing?.enabled);
  const dailyMemoryEnabled = Boolean(memory.daily_log?.enabled);
  const proactiveExtractEnabled = Boolean(memory.proactive_extract?.enabled);
  const proactiveMinToolCalls = Number(memory.proactive_extract?.min_tool_calls ?? 1);
  const ttsEnabled = Boolean(tts.enabled);
  const ttsChannelText = Array.isArray(tts.enabled_channels) ? tts.enabled_channels.join(', ') : '';
  const briefingOutputChannel = automation.daily_briefing?.output?.channel ?? '';
  const briefingOutputPeer = automation.daily_briefing?.output?.peer ?? '';

  // Build config view (redacted JSON)
  const configJson = JSON.stringify(config, null, 2);

  // Build tool list
  const toolList = tools?.tools ?? [];

  // Build services list
  const serviceList = services?.services ?? [];

  _el.innerHTML = `
    <h1 class="text-2xl font-semibold text-zinc-50 mb-6">Settings</h1>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">Personal Assistant Mode</h2>
    <div class="mb-8">
      <div class="grid grid-cols-1 md:grid-cols-2 gap-3 mb-4">
        <label class="flex items-center gap-2.5 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400 cursor-pointer min-h-[44px]" title="When enabled, automations send short announce-style updates before full messages.">
          <input id="assist-delivery-announce" type="checkbox" ${deliveryMode === 'announce' ? 'checked' : ''} title="Toggle announce delivery mode for automations." />
          <span>Automation announce delivery mode</span>
        </label>
        <label class="flex items-center gap-2.5 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400 cursor-pointer min-h-[44px]" title="Turns on the scheduled daily briefing automation.">
          <input id="assist-daily-briefing" type="checkbox" ${dailyBriefingEnabled ? 'checked' : ''} title="Enable or disable the daily briefing job." />
          <span>Daily briefing enabled</span>
        </label>
        <label class="flex items-center gap-2.5 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400 cursor-pointer min-h-[44px]" title="Writes a daily memory summary to help long-term recall.">
          <input id="assist-memory-daily" type="checkbox" ${dailyMemoryEnabled ? 'checked' : ''} title="Enable daily memory log extraction." />
          <span>Daily memory logging</span>
        </label>
        <label class="flex items-center gap-2.5 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400 cursor-pointer min-h-[44px]" title="Extracts memories automatically after tool-heavy activity.">
          <input id="assist-memory-proactive" type="checkbox" ${proactiveExtractEnabled ? 'checked' : ''} title="Enable proactive memory extraction." />
          <span>Proactive memory extraction</span>
        </label>
        <label class="flex flex-col gap-2 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400" title="Minimum number of tool calls before proactive extraction runs.">
          <span>Proactive extract tool-call threshold</span>
          <input id="assist-memory-min-tools" type="number" min="0" max="50" value="${Number.isFinite(proactiveMinToolCalls) ? proactiveMinToolCalls : 1}" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm focus:border-blue-500 outline-none" title="Set a value from 0 to 50 tool calls." />
        </label>
        <label class="flex items-center gap-2.5 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400 cursor-pointer min-h-[44px]" title="Allows the assistant to send text-to-speech voice replies.">
          <input id="assist-tts-enabled" type="checkbox" ${ttsEnabled ? 'checked' : ''} title="Enable voice replies through TTS." />
          <span>TTS voice replies enabled</span>
        </label>
        <label class="flex flex-col gap-2 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400" title="Optional channel allowlist for TTS. Leave blank to allow all channels.">
          <span>TTS channels (comma-separated, blank = all)</span>
          <input id="assist-tts-channels" type="text" value="${escapeHtml(ttsChannelText)}" placeholder="telegram,discord,whatsapp" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm focus:border-blue-500 outline-none" title="Example: telegram,discord,whatsapp" />
        </label>
        <label class="flex flex-col gap-2 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400" title="Channel used for daily briefing delivery, such as telegram or slack.">
          <span>Briefing output channel</span>
          <input id="assist-briefing-channel" type="text" value="${escapeHtml(briefingOutputChannel)}" placeholder="telegram" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm focus:border-blue-500 outline-none" title="Set the channel adapter name for briefings." />
        </label>
        <label class="flex flex-col gap-2 bg-zinc-900 border border-zinc-800 rounded-lg px-3 py-2.5 text-sm text-zinc-400" title="Destination peer/chat identifier used by the selected channel.">
          <span>Briefing output peer/chat id</span>
          <input id="assist-briefing-peer" type="text" value="${escapeHtml(briefingOutputPeer)}" placeholder="123456789" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm focus:border-blue-500 outline-none" title="Use the chat ID, channel ID, or user ID for your adapter." />
        </label>
      </div>
      <div class="flex items-center gap-3 mt-2">
        <button id="assistant-mode-save" class="px-4 py-2 text-sm font-medium rounded-md bg-blue-500 text-zinc-950 hover:opacity-85 transition-opacity">Save Assistant Mode</button>
        <span id="assistant-mode-status" class="text-sm text-zinc-500"></span>
      </div>
    </div>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">WebChat Push Notifications</h2>
    <div class="mb-8">
      ${isPushSupported() ? '' : '<div class="text-sm text-zinc-500 mb-2">This browser does not support PushManager APIs.</div>'}
      <div class="flex gap-2 mb-2">
        <button id="push-enable" class="px-4 py-2 text-sm font-medium rounded-md bg-blue-500 text-zinc-950 hover:opacity-85 transition-opacity" type="button">Enable Push</button>
        <button id="push-disable" class="px-3 py-1.5 text-sm font-medium rounded-md border border-zinc-700 bg-zinc-800 text-zinc-200 hover:bg-zinc-700 transition-colors" type="button">Disable Push</button>
      </div>
      <div id="push-status" class="text-sm text-zinc-500"></div>
    </div>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">Hook Patterns</h2>
    <div class="mb-8">
      <div class="flex flex-col gap-3">
        <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-3">
          <label class="block text-xs font-medium text-zinc-400 uppercase tracking-wide mb-2" title="One glob pattern per line. Matching tools require explicit approval.">Confirm (require approval)</label>
          <textarea id="hooks-confirm" rows="3" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm font-mono focus:border-blue-500 outline-none resize-y min-h-[60px]" title="Example: tool:group:fs/**/*">${escapeHtml(confirmPatterns.join('\n'))}</textarea>
        </div>
        <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-3">
          <label class="block text-xs font-medium text-zinc-400 uppercase tracking-wide mb-2" title="Matching tools run without approval, but each call is logged.">Log (allow + log)</label>
          <textarea id="hooks-log" rows="3" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm font-mono focus:border-blue-500 outline-none resize-y min-h-[60px]" title="Use one glob pattern per line.">${escapeHtml(logPatterns.join('\n'))}</textarea>
        </div>
        <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-3">
          <label class="block text-xs font-medium text-zinc-400 uppercase tracking-wide mb-2" title="Matching tools run with no prompt and no audit log entry.">Silent (allow silently)</label>
          <textarea id="hooks-silent" rows="3" class="w-full bg-zinc-950 text-zinc-50 border border-zinc-800 rounded-md px-3 py-2 text-sm font-mono focus:border-blue-500 outline-none resize-y min-h-[60px]" title="Use carefully. One glob pattern per line.">${escapeHtml(silentPatterns.join('\n'))}</textarea>
        </div>
        <div class="flex items-center gap-3 mt-2">
          <button id="hooks-save" class="px-4 py-2 text-sm font-medium rounded-md bg-blue-500 text-zinc-950 hover:opacity-85 transition-opacity">Save Hook Patterns</button>
          <span id="hooks-status" class="text-sm text-zinc-500"></span>
        </div>
      </div>
    </div>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">Tools (${toolList.length})</h2>
    <div class="mb-8">
      ${toolList.length > 0 ? `
        <div class="overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr>
                <th class="text-left px-3 py-2 text-xs font-medium text-zinc-400 uppercase tracking-wide border-b border-zinc-800">Name</th>
                <th class="text-left px-3 py-2 text-xs font-medium text-zinc-400 uppercase tracking-wide border-b border-zinc-800">Description</th>
              </tr>
            </thead>
            <tbody>
              ${toolList.map(t => `
                <tr>
                  <td class="px-3 py-2 border-b border-zinc-800/50"><code class="font-mono text-blue-500 text-xs">${escapeHtml(t.name)}</code></td>
                  <td class="px-3 py-2 border-b border-zinc-800/50 text-zinc-400">${escapeHtml(t.description ?? '')}</td>
                </tr>
              `).join('')}
            </tbody>
          </table>
        </div>
      ` : '<div class="text-center py-12 px-6 text-zinc-500 text-sm">No tools available</div>'}
    </div>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">Services</h2>
    <div class="mb-8">
      ${serviceList.length > 0 ? `
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
          ${serviceList.map(svc => {
    const typeIcon = svc.type === 'channel' ? '📡' : svc.type === 'automation' ? '⚙️' : '🔧';
    const borderClass = svc.status === 'connected'
      ? 'border-l-green-500'
      : svc.status === 'configured'
        ? 'border-l-blue-500'
        : svc.status === 'error'
          ? 'border-l-red-500'
          : 'border-l-zinc-600 opacity-60';
    const statusColorClass = svc.status === 'connected'
      ? 'text-green-500'
      : svc.status === 'configured'
        ? 'text-blue-500'
        : svc.status === 'error'
          ? 'text-red-500'
          : 'text-zinc-500';
    const itemCount = svc.itemCount ? ` (${svc.itemCount})` : '';
    return `
              <div class="bg-zinc-900 border border-zinc-800 rounded-lg p-3 flex flex-col gap-1 border-l-4 ${borderClass}">
                <span>${typeIcon}</span>
                <span class="text-sm font-semibold text-zinc-50">${escapeHtml(svc.name)}${itemCount}</span>
                <span class="text-xs uppercase ${statusColorClass}">${escapeHtml(svc.status)}</span>
                <span class="text-xs text-zinc-500">${escapeHtml(svc.description ?? '')}</span>
              </div>
            `;
  }).join('')}
        </div>
      ` : '<div class="text-sm text-zinc-500">No services found</div>'}
    </div>

    <h2 class="text-lg font-semibold text-zinc-50 mb-4 mt-8 pb-2 border-b border-zinc-800">Configuration (read-only)</h2>
    <div class="mb-8">
      <div class="bg-zinc-950 border border-zinc-800 rounded-lg p-4 max-h-96 overflow-y-auto font-mono text-xs text-zinc-400 whitespace-pre-wrap"><code>${escapeHtml(configJson)}</code></div>
    </div>
  `;

  // Bind save hooks
  _el.querySelector('#hooks-save').addEventListener('click', saveHooks);
  _el.querySelector('#assistant-mode-save').addEventListener('click', saveAssistantMode);
  _el.querySelector('#push-enable').addEventListener('click', onEnablePush);
  _el.querySelector('#push-disable').addEventListener('click', onDisablePush);
  await renderPushStatus();
}

async function saveAssistantMode() {
  const status = _el.querySelector('#assistant-mode-status');
  status.textContent = 'Saving...';
  status.className = 'text-sm text-zinc-500';

  const useAnnounce = Boolean(_el.querySelector('#assist-delivery-announce')?.checked);
  const dailyBriefing = Boolean(_el.querySelector('#assist-daily-briefing')?.checked);
  const memoryDaily = Boolean(_el.querySelector('#assist-memory-daily')?.checked);
  const memoryProactive = Boolean(_el.querySelector('#assist-memory-proactive')?.checked);
  const ttsEnabled = Boolean(_el.querySelector('#assist-tts-enabled')?.checked);
  const minToolsRaw = Number.parseInt(_el.querySelector('#assist-memory-min-tools')?.value ?? '1', 10);
  const minTools = Number.isFinite(minToolsRaw) ? Math.min(50, Math.max(0, minToolsRaw)) : 1;
  const ttsChannelsRaw = _el.querySelector('#assist-tts-channels')?.value ?? '';
  const briefingChannel = (_el.querySelector('#assist-briefing-channel')?.value ?? '').trim();
  const briefingPeer = (_el.querySelector('#assist-briefing-peer')?.value ?? '').trim();
  const ttsChannels = ttsChannelsRaw
    .split(',')
    .map((value) => value.trim())
    .filter(Boolean);

  const patches = {
    'automation.delivery_mode': useAnnounce ? 'announce' : 'shared_session',
    'automation.daily_briefing.enabled': dailyBriefing,
    'memory.daily_log.enabled': memoryDaily,
    'memory.proactive_extract.enabled': memoryProactive,
    'memory.proactive_extract.min_tool_calls': minTools,
    'tts.enabled': ttsEnabled,
    'tts.enabled_channels': ttsChannels,
  };
  if (briefingChannel) {
    patches['automation.daily_briefing.output.channel'] = briefingChannel;
  }
  if (briefingPeer) {
    patches['automation.daily_briefing.output.peer'] = briefingPeer;
  }

  try {
    const result = await _client.call('config.patch', { patches });
    const applied = result.applied ?? [];
    const rejected = result.rejected ?? [];
    const persisted = result.persisted === true;
    const persistError = result.persistError;

    if (rejected.length > 0) {
      status.textContent = `Partially saved. Rejected: ${rejected.join(', ')}`;
      status.className = 'text-sm text-red-500';
    } else if (persistError) {
      status.textContent = `Save failed: ${persistError}`;
      status.className = 'text-sm text-red-500';
    } else if (!persisted) {
      status.textContent = `Saved in runtime only (${applied.length} updated)`;
      status.className = 'text-sm text-zinc-500';
    } else {
      status.textContent = `Saved (${applied.length} updated)`;
      status.className = 'text-sm text-green-500';
      if (_settingsCache && _settingsCache.automation) {
        _settingsCache.automation.delivery_mode = useAnnounce ? 'announce' : 'shared_session';
      }
    }
  } catch (err) {
    status.textContent = `Error: ${err.message}`;
    status.className = 'text-sm text-red-500';
  }

  setTimeout(() => {
    if (status) {status.textContent = '';}
  }, 5000);
}

async function saveHooks() {
  const status = _el.querySelector('#hooks-status');
  status.textContent = 'Saving...';
  status.className = 'text-sm text-zinc-500';

  try {
    const confirm = _el.querySelector('#hooks-confirm').value.split('\n').map(s => s.trim()).filter(Boolean);
    const log = _el.querySelector('#hooks-log').value.split('\n').map(s => s.trim()).filter(Boolean);
    const silent = _el.querySelector('#hooks-silent').value.split('\n').map(s => s.trim()).filter(Boolean);

    const result = await _client.call('config.patch', {
      patches: {
        'hooks.confirm': confirm,
        'hooks.log': log,
        'hooks.silent': silent,
      },
    });

    const applied = result.applied ?? [];
    const rejected = result.rejected ?? [];
    const persisted = result.persisted === true;
    const persistError = result.persistError;

    if (rejected.length > 0) {
      status.textContent = `Partially saved. Rejected: ${rejected.join(', ')}`;
      status.className = 'text-sm text-red-500';
    } else if (persistError) {
      status.textContent = `Save failed: ${persistError}`;
      status.className = 'text-sm text-red-500';
    } else if (!persisted) {
      status.textContent = `Saved in runtime only (${applied.length} updated)`;
      status.className = 'text-sm text-zinc-500';
    } else {
      status.textContent = `Saved (${applied.length} updated)`;
      status.className = 'text-sm text-green-500';
    }
  } catch (err) {
    status.textContent = `Error: ${err.message}`;
    status.className = 'text-sm text-red-500';
  }

  // Clear status after 5s
  setTimeout(() => {
    if (status) {status.textContent = '';}
  }, 5000);
}

export const SettingsPage = {
  async render(el, client) {
    _client = client;
    _el = el;
    await loadSettings();
  },

  teardown() {
    _client = null;
    _el = null;
    _settingsCache = null;
  },
};
