import * as vscode from 'vscode';
import { listModels } from '@lib/providers';
import {
  SECRET_API_KEY,
  clearApiKey,
  hasApiKey,
  readSettings,
  settingsCatalog,
  storeApiKey,
  updateSettings,
} from './config';
import { refreshStatusBar } from './statusBar';

type HostToWeb =
  | {
      type: 'state';
      settings: ReturnType<typeof readSettings>;
      catalog: ReturnType<typeof settingsCatalog>;
      hasApiKey: boolean;
      models: string[];
      modelsError?: string;
      status?: string;
    }
  | { type: 'models'; models: string[]; modelsError?: string };

type WebToHost =
  | { type: 'ready' }
  | { type: 'saveSettings'; patch: Record<string, string> }
  | { type: 'saveApiKey'; provider: string; apiKey: string }
  | { type: 'clearApiKey' }
  | { type: 'refreshModels' };

let panel: vscode.WebviewPanel | undefined;

export function openSettingsPanel(context: vscode.ExtensionContext): void {
  if (panel) {
    panel.reveal(vscode.ViewColumn.One);
    void pushState(context, panel.webview);
    return;
  }

  panel = vscode.window.createWebviewPanel(
    'lexaiSettings',
    'LexAI Settings',
    vscode.ViewColumn.One,
    { enableScripts: true, retainContextWhenHidden: true },
  );

  panel.iconPath = undefined;
  panel.webview.html = getHtml(panel.webview);
  panel.onDidDispose(() => {
    panel = undefined;
  });

  panel.webview.onDidReceiveMessage(async (msg: WebToHost) => {
    if (!panel) return;
    try {
      switch (msg.type) {
        case 'ready':
          await pushState(context, panel.webview);
          break;
        case 'saveSettings':
          await updateSettings(msg.patch);
          await pushState(context, panel.webview, 'Saved.');
          break;
        case 'saveApiKey':
          if (!msg.apiKey.trim()) {
            await pushState(context, panel.webview, 'API key cannot be empty.');
            return;
          }
          await storeApiKey(context, msg.apiKey, msg.provider);
          await updateSettings({ provider: msg.provider });
          await refreshStatusBar();
          await pushState(context, panel.webview, 'API key saved.');
          break;
        case 'clearApiKey':
          await clearApiKey(context);
          await refreshStatusBar();
          await pushState(context, panel.webview, 'API key cleared.');
          break;
        case 'refreshModels':
          await pushModels(context, panel.webview);
          break;
      }
    } catch (err) {
      await pushState(context, panel.webview, `Error: ${String(err)}`);
    }
  });
}

async function pushState(
  context: vscode.ExtensionContext,
  webview: vscode.Webview,
  status?: string,
): Promise<void> {
  const settings = readSettings();
  const keyed = await hasApiKey(context);
  const modelResult = keyed
    ? await listModels(settings.provider, (await context.secrets.get(SECRET_API_KEY)) ?? undefined)
    : { models: [] as string[], error: 'Set an API key to load models.' };

  const payload: HostToWeb = {
    type: 'state',
    settings,
    catalog: settingsCatalog(),
    hasApiKey: keyed,
    models: modelResult.models ?? [],
    modelsError: modelResult.error,
    status,
  };
  await webview.postMessage(payload);
}

async function pushModels(
  context: vscode.ExtensionContext,
  webview: vscode.Webview,
): Promise<void> {
  const settings = readSettings();
  const apiKey = await context.secrets.get(SECRET_API_KEY);
  const modelResult = await listModels(settings.provider, apiKey ?? undefined);
  const payload: HostToWeb = {
    type: 'models',
    models: modelResult.models ?? [],
    modelsError: modelResult.error,
  };
  await webview.postMessage(payload);
}

function getHtml(webview: vscode.Webview): string {
  const csp = [
    `default-src 'none'`,
    `style-src ${webview.cspSource} 'unsafe-inline'`,
    `script-src ${webview.cspSource} 'unsafe-inline'`,
  ].join('; ');

  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="Content-Security-Policy" content="${csp}" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>LexAI Settings</title>
  <style>
    :root {
      color-scheme: light dark;
    }
    body {
      font-family: var(--vscode-font-family);
      font-size: var(--vscode-font-size);
      color: var(--vscode-foreground);
      background: var(--vscode-editor-background);
      margin: 0;
      padding: 20px 24px 40px;
      max-width: 560px;
    }
    h1 { font-size: 1.35rem; font-weight: 700; margin: 0 0 4px; }
    .sub { opacity: 0.75; margin-bottom: 20px; font-size: 0.9rem; }
    section {
      border: 1px solid var(--vscode-widget-border, rgba(128,128,128,0.35));
      border-radius: 8px;
      padding: 14px 16px;
      margin-bottom: 14px;
      background: var(--vscode-sideBar-background, transparent);
    }
    section h2 {
      font-size: 0.75rem;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      margin: 0 0 12px;
      opacity: 0.8;
    }
    label { display: block; font-size: 0.85rem; margin-bottom: 4px; opacity: 0.9; }
    .field { margin-bottom: 12px; }
    .field:last-child { margin-bottom: 0; }
    select, input[type="text"], input[type="password"] {
      width: 100%;
      box-sizing: border-box;
      padding: 8px 10px;
      border-radius: 6px;
      border: 1px solid var(--vscode-input-border, rgba(128,128,128,0.45));
      background: var(--vscode-input-background);
      color: var(--vscode-input-foreground);
    }
    .row { display: flex; gap: 8px; align-items: center; }
    .row select { flex: 1; }
    button {
      padding: 7px 12px;
      border-radius: 6px;
      border: 1px solid var(--vscode-button-border, transparent);
      background: var(--vscode-button-background);
      color: var(--vscode-button-foreground);
      cursor: pointer;
    }
    button.secondary {
      background: var(--vscode-button-secondaryBackground);
      color: var(--vscode-button-secondaryForeground);
    }
    button:disabled { opacity: 0.5; cursor: default; }
    .hint { font-size: 0.8rem; opacity: 0.7; margin-top: 6px; }
    .status {
      min-height: 1.2em;
      margin-bottom: 12px;
      color: var(--vscode-testing-iconPassed, #3fb950);
      font-size: 0.85rem;
    }
    .badge {
      display: inline-block;
      padding: 2px 8px;
      border-radius: 999px;
      font-size: 0.75rem;
      background: var(--vscode-badge-background);
      color: var(--vscode-badge-foreground);
    }
    .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
    #customPersonaWrap { display: none; }
  </style>
</head>
<body>
  <h1>LexAI Settings</h1>
  <p class="sub">Provider, writing style, and Prompt Builder parameters. API key stays in Secret Storage.</p>
  <div class="status" id="status"></div>

  <section>
    <h2>Provider</h2>
    <div class="field">
      <label for="provider">Provider</label>
      <select id="provider"></select>
    </div>
    <div class="field">
      <label for="model">Model</label>
      <div class="row">
        <select id="model"></select>
        <button type="button" class="secondary" id="refreshModels">Refresh</button>
      </div>
      <div class="hint" id="modelsHint"></div>
    </div>
    <div class="field">
      <label>API key <span class="badge" id="keyBadge">…</span></label>
      <input id="apiKey" type="password" placeholder="Paste new key to replace" autocomplete="off" />
      <div class="actions">
        <button type="button" id="saveKey">Save API key</button>
        <button type="button" class="secondary" id="clearKey">Clear key</button>
      </div>
    </div>
  </section>

  <section>
    <h2>Writing</h2>
    <div class="field">
      <label for="writingStyle">Writing style</label>
      <select id="writingStyle"></select>
      <div class="hint">Used by Fix, Rephrase, Shorten, Expand, Explain.</div>
    </div>
  </section>

  <section>
    <h2>Prompt Builder</h2>
    <div class="field">
      <label for="promptPattern">Pattern</label>
      <select id="promptPattern"></select>
      <div class="hint" id="patternHint"></div>
    </div>
    <div class="field">
      <label for="promptPersona">Persona</label>
      <select id="promptPersona"></select>
    </div>
    <div class="field" id="customPersonaWrap">
      <label for="customPersona">Custom persona</label>
      <input id="customPersona" type="text" placeholder="e.g. Staff engineer who writes crisp RFCs" />
    </div>
    <div class="field">
      <label for="promptFormat">Output format</label>
      <select id="promptFormat"></select>
    </div>
    <div class="field">
      <label for="promptModel">Prompt model override</label>
      <select id="promptModel"></select>
      <div class="hint">Optional. Empty = use the provider model above when running Make Prompt.</div>
    </div>
  </section>

  <div class="actions">
    <button type="button" id="saveAll">Save settings</button>
  </div>

  <script>
    const vscode = acquireVsCodeApi();
    const $ = (id) => document.getElementById(id);

    let catalog = null;
    let models = [];

    function fillSelect(el, options, selected, mapFn) {
      el.innerHTML = '';
      for (const opt of options) {
        const { value, label } = mapFn(opt);
        const o = document.createElement('option');
        o.value = value;
        o.textContent = label;
        if (value === selected) o.selected = true;
        el.appendChild(o);
      }
    }

    function syncCustomPersona() {
      $('customPersonaWrap').style.display =
        $('promptPersona').value === 'Custom…' ? 'block' : 'none';
    }

    function syncPatternHint() {
      const id = $('promptPattern').value;
      const p = (catalog?.promptPatterns || []).find((x) => x.id === id);
      $('patternHint').textContent = p?.hint || '';
    }

    function fillModelSelects(settings) {
      const modelOpts = ['', ...models];
      fillSelect($('model'), modelOpts, settings.model || '', (m) => ({
        value: m,
        label: m || '(provider default)',
      }));
      // Keep a typed/custom model even if not in the live list.
      if (settings.model && !modelOpts.includes(settings.model)) {
        const o = document.createElement('option');
        o.value = settings.model;
        o.textContent = settings.model;
        o.selected = true;
        $('model').appendChild(o);
      }
      fillSelect($('promptModel'), modelOpts, settings.promptModel || '', (m) => ({
        value: m,
        label: m || '(same as provider model)',
      }));
      if (settings.promptModel && !modelOpts.includes(settings.promptModel)) {
        const o = document.createElement('option');
        o.value = settings.promptModel;
        o.textContent = settings.promptModel;
        o.selected = true;
        $('promptModel').appendChild(o);
      }
    }

    function applyState(msg) {
      catalog = msg.catalog;
      models = msg.models || [];
      const s = msg.settings;
      fillSelect($('provider'), catalog.providers, s.provider, (p) => ({
        value: p.id,
        label: p.label,
      }));
      fillSelect($('writingStyle'), catalog.writingStyles, s.writingStyle, (v) => ({
        value: v,
        label: v,
      }));
      fillSelect($('promptPattern'), catalog.promptPatterns, s.promptPattern, (p) => ({
        value: p.id,
        label: p.group ? p.group + ' — ' + p.label : p.label,
      }));
      fillSelect($('promptPersona'), catalog.promptPersonas, s.promptPersona, (v) => ({
        value: v,
        label: v,
      }));
      fillSelect($('promptFormat'), catalog.promptFormats, s.promptFormat, (v) => ({
        value: v,
        label: v,
      }));
      $('customPersona').value = s.customPersona || '';
      fillModelSelects(s);
      $('keyBadge').textContent = msg.hasApiKey ? 'set' : 'not set';
      $('modelsHint').textContent = msg.modelsError || (models.length ? models.length + ' models' : '');
      $('status').textContent = msg.status || '';
      syncCustomPersona();
      syncPatternHint();
    }

    function collectPatch() {
      return {
        provider: $('provider').value,
        model: $('model').value,
        writingStyle: $('writingStyle').value,
        promptPattern: $('promptPattern').value,
        promptPersona: $('promptPersona').value,
        customPersona: $('customPersona').value,
        promptFormat: $('promptFormat').value,
        promptModel: $('promptModel').value,
      };
    }

    window.addEventListener('message', (event) => {
      const msg = event.data;
      if (msg.type === 'state') applyState(msg);
      if (msg.type === 'models') {
        models = msg.models || [];
        fillModelSelects(collectPatch());
        $('modelsHint').textContent = msg.modelsError || (models.length ? models.length + ' models' : '');
      }
    });

    $('promptPersona').addEventListener('change', syncCustomPersona);
    $('promptPattern').addEventListener('change', syncPatternHint);
    $('provider').addEventListener('change', () => {
      vscode.postMessage({ type: 'saveSettings', patch: { provider: $('provider').value } });
    });
    $('saveAll').addEventListener('click', () => {
      vscode.postMessage({ type: 'saveSettings', patch: collectPatch() });
    });
    $('saveKey').addEventListener('click', () => {
      vscode.postMessage({
        type: 'saveApiKey',
        provider: $('provider').value,
        apiKey: $('apiKey').value,
      });
      $('apiKey').value = '';
    });
    $('clearKey').addEventListener('click', () => {
      vscode.postMessage({ type: 'clearApiKey' });
    });
    $('refreshModels').addEventListener('click', () => {
      vscode.postMessage({ type: 'refreshModels' });
    });

    vscode.postMessage({ type: 'ready' });
  </script>
</body>
</html>`;
}
