import * as vscode from 'vscode';
import {
  ACTIONS,
  ACTION_LABELS,
  MIN_SELECTION_LENGTH,
  resolvePromptPersona,
  type ActionId,
} from '@lib/actions';
import { runCodeAssist } from './codeAssist';
import { hasApiKey, readSettings, resolveConfig, settingsCatalog } from './config';
import { generateSuggestion } from './llm';
import { withLexAIProgress } from './statusBar';

const VIEW_ID = 'lexai.sidebar';

export function registerSidebarView(context: vscode.ExtensionContext): void {
  const provider = new LexAISidebarProvider(context);
  context.subscriptions.push(
    vscode.window.registerWebviewViewProvider(VIEW_ID, provider, {
      webviewOptions: { retainContextWhenHidden: true },
    }),
    vscode.commands.registerCommand('lexai.openSidebar', async () => {
      await vscode.commands.executeCommand(`${VIEW_ID}.focus`);
    }),
  );
}

class LexAISidebarProvider implements vscode.WebviewViewProvider {
  private view?: vscode.WebviewView;

  constructor(private readonly context: vscode.ExtensionContext) {}

  resolveWebviewView(webviewView: vscode.WebviewView): void {
    this.view = webviewView;
    const logoUri = webviewView.webview.asWebviewUri(
      vscode.Uri.joinPath(this.context.extensionUri, 'media', 'icon.svg'),
    );
    webviewView.webview.options = {
      enableScripts: true,
      localResourceRoots: [vscode.Uri.joinPath(this.context.extensionUri, 'media')],
    };
    webviewView.webview.html = getHtml(webviewView.webview, logoUri.toString());

    webviewView.webview.onDidReceiveMessage(async (msg) => {
      switch (msg.type) {
        case 'ready':
          await this.pushState();
          break;
        case 'run':
          if (msg.action === 'codeAssist') {
            await this.runCodeAssistFromSidebar(String(msg.text ?? ''));
            break;
          }
          await this.run(msg.action as ActionId, String(msg.text ?? ''), msg.options ?? {});
          break;
        case 'insertSelection':
          await this.insertSelection();
          break;
        case 'openSettings':
          await vscode.commands.executeCommand('lexai.openSettings');
          break;
        case 'copy':
          await vscode.env.clipboard.writeText(String(msg.text ?? ''));
          await webviewView.webview.postMessage({ type: 'copied' });
          break;
      }
    });
  }

  private async pushState(extra?: { status?: string; error?: string; result?: string }): Promise<void> {
    if (!this.view) return;
    const settings = readSettings();
    const keyed = await hasApiKey(this.context);
    await this.view.webview.postMessage({
      type: 'state',
      catalog: settingsCatalog(),
      settings: {
        writingStyle: settings.writingStyle,
        promptPattern: settings.promptPattern,
        promptPersona: settings.promptPersona,
        promptFormat: settings.promptFormat,
      },
      actions: [
        { id: 'codeAssist', label: 'Code Assist' },
        ...ACTIONS.map((id) => ({ id, label: ACTION_LABELS[id] })),
      ],
      hasApiKey: keyed,
      minLength: MIN_SELECTION_LENGTH,
      status: extra?.status,
      error: extra?.error,
      result: extra?.result,
    });
  }

  private async insertSelection(): Promise<void> {
    const editor = vscode.window.activeTextEditor;
    const text = editor && !editor.selection.isEmpty
      ? editor.document.getText(editor.selection)
      : '';
    if (!this.view) return;
    if (!text.trim()) {
      await this.view.webview.postMessage({
        type: 'status',
        error: 'No editor selection to insert.',
      });
      return;
    }
    await this.view.webview.postMessage({ type: 'setInput', text });
  }

  private async runCodeAssistFromSidebar(instruction: string): Promise<void> {
    if (!this.view) return;
    const goal = instruction.trim();
    if (goal.length < 3) {
      await this.pushState({
        error: 'Describe what you want LexAI to do with the editor selection.',
      });
      return;
    }
    const editor = vscode.window.activeTextEditor;
    if (!editor || editor.selection.isEmpty) {
      await this.pushState({
        error: 'Select code in the editor first (Code Assist uses that selection + related files).',
      });
      return;
    }
    await runCodeAssist(this.context, goal);
    await this.pushState({
      status: 'Code Assist result is in the editor suggestion zone.',
    });
  }

  private async run(
    action: ActionId,
    text: string,
    options: {
      writingStyle?: string;
      promptPattern?: string;
      promptPersona?: string;
      promptFormat?: string;
    },
  ): Promise<void> {
    if (!this.view) return;
    const trimmed = text.trim();
    if (trimmed.length < MIN_SELECTION_LENGTH) {
      await this.pushState({
        error: `Enter at least ${MIN_SELECTION_LENGTH} characters.`,
      });
      return;
    }

    const resolved = await resolveConfig(this.context);
    if (resolved.error || !resolved.config) {
      await this.pushState({ error: resolved.error ?? 'Not configured' });
      return;
    }

    const settings = readSettings();
    const writingStyle = (options.writingStyle || settings.writingStyle) as typeof settings.writingStyle;
    const promptPattern = options.promptPattern || settings.promptPattern;
    const promptPersona = options.promptPersona || settings.promptPersona;
    const promptFormat = options.promptFormat || settings.promptFormat;

    const label = ACTION_LABELS[action];
    const response = await withLexAIProgress(`LexAI: ${label}…`, () =>
      generateSuggestion({
        action,
        text,
        config: resolved.config!,
        writingStyle,
        promptParams: {
          pattern: promptPattern,
          persona: resolvePromptPersona(promptPersona, settings.customPersona),
          format: promptFormat,
        },
        promptModel: settings.promptModel || undefined,
      }),
    );

    if (response.error || !response.result) {
      await this.pushState({ error: response.error ?? 'Empty response.' });
      return;
    }
    await this.pushState({ result: response.result, status: `${label} done.` });
  }
}

function getHtml(webview: vscode.Webview, logoUri: string): string {
  const csp = [
    `default-src 'none'`,
    `img-src ${webview.cspSource} data:`,
    `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</title>
  <style>
    :root {
      --lexai-accent: #818cf8;
      --lexai-accent-strong: #6366f1;
      --lexai-radius: 10px;
      --lexai-gap: 10px;
    }
    * { box-sizing: border-box; }
    body {
      font-family: var(--vscode-font-family);
      font-size: var(--vscode-font-size);
      color: var(--vscode-foreground);
      background: transparent;
      margin: 0;
      padding: 12px 12px 28px;
      line-height: 1.4;
    }

    .hero {
      display: flex;
      align-items: center;
      gap: 12px;
      margin-bottom: 14px;
      padding: 12px;
      border-radius: 12px;
      border: 1px solid color-mix(in srgb, var(--lexai-accent) 35%, var(--vscode-widget-border, transparent));
      background:
        linear-gradient(135deg, color-mix(in srgb, var(--lexai-accent-strong) 18%, transparent), transparent 60%),
        var(--vscode-sideBar-background, transparent);
    }
    .hero img {
      width: 40px;
      height: 40px;
      border-radius: 10px;
      flex-shrink: 0;
      box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35);
    }
    .hero-text { min-width: 0; }
    .hero h1 {
      margin: 0;
      font-size: 1.15rem;
      font-weight: 750;
      letter-spacing: -0.02em;
      color: var(--lexai-accent);
    }
    .hero p {
      margin: 3px 0 0;
      font-size: 0.78rem;
      opacity: 0.78;
    }
    .badge {
      display: inline-flex;
      align-items: center;
      gap: 5px;
      margin-top: 6px;
      font-size: 0.68rem;
      font-weight: 600;
      letter-spacing: 0.03em;
      text-transform: uppercase;
      padding: 2px 8px;
      border-radius: 999px;
      background: color-mix(in srgb, var(--lexai-accent) 22%, transparent);
      color: var(--lexai-accent);
      border: 1px solid color-mix(in srgb, var(--lexai-accent) 40%, transparent);
    }
    .badge.bad {
      background: color-mix(in srgb, var(--vscode-errorForeground) 18%, transparent);
      color: var(--vscode-errorForeground);
      border-color: color-mix(in srgb, var(--vscode-errorForeground) 40%, transparent);
    }
    .badge .dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: currentColor;
    }

    .toolbar {
      display: flex;
      flex-wrap: wrap;
      gap: 6px;
      margin-bottom: 12px;
    }

    .card {
      border: 1px solid var(--vscode-widget-border, rgba(128,128,128,0.28));
      border-radius: var(--lexai-radius);
      background: color-mix(in srgb, var(--vscode-editor-background) 88%, transparent);
      padding: 12px;
      margin-bottom: var(--lexai-gap);
    }
    .card-head {
      display: flex;
      align-items: baseline;
      justify-content: space-between;
      gap: 8px;
      margin-bottom: 8px;
    }
    .card-title {
      margin: 0;
      font-size: 0.68rem;
      font-weight: 700;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      opacity: 0.72;
    }
    .hint {
      font-size: 0.7rem;
      opacity: 0.55;
    }

    label.field {
      display: block;
      font-size: 0.72rem;
      font-weight: 600;
      opacity: 0.8;
      margin: 8px 0 4px;
    }
    label.field:first-child { margin-top: 0; }

    textarea, select {
      width: 100%;
      border-radius: 8px;
      border: 1px solid var(--vscode-input-border, rgba(128,128,128,0.4));
      background: var(--vscode-input-background);
      color: var(--vscode-input-foreground);
      font-family: var(--vscode-editor-font-family, ui-monospace, monospace);
      font-size: 0.84rem;
      padding: 9px 10px;
      outline: none;
    }
    textarea:focus, select:focus {
      border-color: var(--lexai-accent);
      box-shadow: 0 0 0 1px color-mix(in srgb, var(--lexai-accent) 55%, transparent);
    }
    textarea { min-height: 120px; resize: vertical; line-height: 1.4; }
    textarea#output { min-height: 150px; }

    .grid-2 {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: 8px;
    }
    .grid-2 .span-2 { grid-column: 1 / -1; }

    button {
      padding: 7px 11px;
      border-radius: 8px;
      border: 1px solid var(--vscode-button-border, transparent);
      background: var(--vscode-button-background);
      color: var(--vscode-button-foreground);
      cursor: pointer;
      font-size: 0.78rem;
      font-weight: 600;
    }
    button.secondary {
      background: var(--vscode-button-secondaryBackground);
      color: var(--vscode-button-secondaryForeground);
    }
    button.ghost {
      background: transparent;
      border-color: var(--vscode-widget-border, rgba(128,128,128,0.35));
      color: var(--vscode-foreground);
      font-weight: 500;
    }
    button.primary {
      width: 100%;
      margin-top: 10px;
      padding: 10px 12px;
      font-size: 0.86rem;
      background: linear-gradient(135deg, #6366f1, #8b5cf6);
      border: none;
      color: #fff;
      box-shadow: 0 6px 18px rgba(99, 102, 241, 0.28);
    }
    button.primary:hover { filter: brightness(1.05); }
    button:disabled { opacity: 0.45; cursor: default; filter: none; }

    .out-actions {
      display: flex;
      gap: 6px;
      margin-top: 8px;
    }
    .out-actions button { flex: 1; }

    .status {
      min-height: 1.15em;
      margin-top: 10px;
      font-size: 0.78rem;
      opacity: 0.8;
    }
    .status.error {
      color: var(--vscode-errorForeground);
      opacity: 1;
      font-weight: 600;
    }
    .status.ok { color: var(--lexai-accent); opacity: 1; }

    #promptOpts { display: none; }
  </style>
</head>
<body>
  <header class="hero">
    <img src="${logoUri}" alt="LexAI" />
    <div class="hero-text">
      <h1>LexAI</h1>
      <p>Writing help in the sidebar · Code Assist on editor selections with workspace context.</p>
      <span class="badge" id="keyBadge"><span class="dot"></span><span id="keyLabel">…</span></span>
    </div>
  </header>

  <div class="toolbar">
    <button type="button" class="ghost" id="insertSel">Insert selection</button>
    <button type="button" class="ghost" id="clearIn">Clear</button>
    <button type="button" class="ghost" id="settings">Settings</button>
  </div>

  <section class="card">
    <div class="card-head">
      <h2 class="card-title">Input</h2>
      <span class="hint" id="charHint"></span>
    </div>
    <textarea id="input" placeholder="Paste text from your terminal, notes, or draft…"></textarea>
    <p class="hint" id="inputHint" style="margin:6px 0 0"></p>
  </section>

  <section class="card">
    <div class="card-head">
      <h2 class="card-title">Action</h2>
    </div>
    <label class="field" for="action">What should LexAI do?</label>
    <select id="action"></select>

    <div id="styleOpts">
      <label class="field" for="writingStyle">Writing style</label>
      <select id="writingStyle"></select>
    </div>
    <div id="promptOpts">
      <div class="grid-2">
        <div class="span-2">
          <label class="field" for="promptPattern">Pattern</label>
          <select id="promptPattern"></select>
        </div>
        <div>
          <label class="field" for="promptPersona">Persona</label>
          <select id="promptPersona"></select>
        </div>
        <div>
          <label class="field" for="promptFormat">Format</label>
          <select id="promptFormat"></select>
        </div>
      </div>
    </div>

    <button type="button" class="primary" id="run">Run LexAI</button>
  </section>

  <section class="card">
    <div class="card-head">
      <h2 class="card-title">Output</h2>
      <span class="hint">Read-only · copy when ready</span>
    </div>
    <textarea id="output" readonly placeholder="Result appears here…"></textarea>
    <div class="out-actions">
      <button type="button" id="copy" disabled>Copy</button>
      <button type="button" class="secondary" id="useAsInput" disabled>Use as input</button>
    </div>
  </section>

  <div class="status" id="status"></div>

  <script>
    const vscode = acquireVsCodeApi();
    const $ = (id) => document.getElementById(id);
    let busy = false;
    let minLength = 10;

    function fillSelect(el, options, selected, mapFn) {
      const prev = selected || el.value;
      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 === prev) o.selected = true;
        el.appendChild(o);
      }
    }

    function syncActionUi() {
      const action = $('action').value;
      const isPrompt = action === 'prompt';
      const isCodeAssist = action === 'codeAssist';
      $('promptOpts').style.display = isPrompt ? 'block' : 'none';
      $('styleOpts').style.display = isPrompt || isCodeAssist ? 'none' : 'block';
      $('input').placeholder = isCodeAssist
        ? 'e.g. Explain how this uses AuthService · Refactor to async/await'
        : 'Paste text from your terminal, notes, or draft…';
      $('inputHint').textContent = isCodeAssist
        ? 'Uses the active editor selection + related files (imports/defs). Select code first.'
        : '';
      $('run').textContent = isCodeAssist ? 'Run Code Assist' : 'Run LexAI';
    }

    function updateCharHint() {
      const n = $('input').value.trim().length;
      $('charHint').textContent = n ? n + ' chars' : '';
    }

    function setBusy(next) {
      busy = next;
      $('run').disabled = next;
      $('run').textContent = next ? 'Working…' : 'Run LexAI';
    }

    function setStatus(text, kind) {
      $('status').textContent = text || '';
      $('status').className = 'status' + (kind === 'error' ? ' error' : kind === 'ok' ? ' ok' : '');
    }

    function applyState(msg) {
      minLength = msg.minLength || 10;
      const ready = !!msg.hasApiKey;
      $('keyLabel').textContent = ready ? 'Ready' : 'API key needed';
      $('keyBadge').className = 'badge' + (ready ? '' : ' bad');

      fillSelect($('action'), msg.actions, $('action').value || 'fix', (a) => ({
        value: a.id, label: a.label,
      }));
      fillSelect($('writingStyle'), msg.catalog.writingStyles, msg.settings.writingStyle, (v) => ({ value: v, label: v }));
      fillSelect($('promptPattern'), msg.catalog.promptPatterns, msg.settings.promptPattern, (p) => ({
        value: p.id, label: p.label,
      }));
      fillSelect(
        $('promptPersona'),
        msg.catalog.promptPersonas.filter((p) => p !== 'Custom…'),
        msg.settings.promptPersona,
        (v) => ({ value: v, label: v }),
      );
      fillSelect($('promptFormat'), msg.catalog.promptFormats, msg.settings.promptFormat, (v) => ({ value: v, label: v }));
      syncActionUi();
      updateCharHint();

      if (msg.result !== undefined) {
        $('output').value = msg.result || '';
        $('copy').disabled = !msg.result;
        $('useAsInput').disabled = !msg.result;
      }
      if (msg.error) setStatus(msg.error, 'error');
      else if (msg.status) setStatus(msg.status, 'ok');
    }

    window.addEventListener('message', (event) => {
      const msg = event.data;
      if (msg.type === 'state') { setBusy(false); applyState(msg); }
      if (msg.type === 'setInput') {
        $('input').value = msg.text || '';
        updateCharHint();
        setStatus('Inserted editor selection.', 'ok');
      }
      if (msg.type === 'status') {
        setBusy(false);
        setStatus(msg.error || msg.status || '', msg.error ? 'error' : 'ok');
      }
      if (msg.type === 'copied') setStatus('Copied to clipboard.', 'ok');
    });

    $('action').addEventListener('change', syncActionUi);
    $('input').addEventListener('input', updateCharHint);
    $('run').addEventListener('click', () => {
      if (busy) return;
      setBusy(true);
      setStatus('Working…', '');
      vscode.postMessage({
        type: 'run',
        action: $('action').value,
        text: $('input').value,
        options: {
          writingStyle: $('writingStyle').value,
          promptPattern: $('promptPattern').value,
          promptPersona: $('promptPersona').value,
          promptFormat: $('promptFormat').value,
        },
      });
    });
    $('copy').addEventListener('click', () => {
      vscode.postMessage({ type: 'copy', text: $('output').value });
    });
    $('useAsInput').addEventListener('click', () => {
      $('input').value = $('output').value;
      updateCharHint();
      setStatus('Moved output into input.', 'ok');
    });
    $('insertSel').addEventListener('click', () => vscode.postMessage({ type: 'insertSelection' }));
    $('clearIn').addEventListener('click', () => { $('input').value = ''; updateCharHint(); });
    $('settings').addEventListener('click', () => vscode.postMessage({ type: 'openSettings' }));

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