import * as vscode from 'vscode';
import { resolvePromptPersona, type ActionId } from '@lib/actions';
import { callProvider } from '@lib/providers';
import { resolveConfig, settingsCatalog, updateSettings } from './config';
import { generateSuggestion } from './llm';
import {
  actionLabel,
  applySuggestionReplace,
  type SuggestionSession,
} from './session';

export type { SuggestionSession } from './session';

const CODE_ASSIST_SYSTEM = [
  'You are LexAI Code Assist, a senior engineer working inside the user\'s IDE.',
  'Follow the user instruction using the provided workspace context.',
  'If changing code: return ONLY the replacement for the selection (no fences).',
  'If explaining: return a clear explanation.',
].join(' ');

type HostToWeb =
  | {
      type: 'state';
      action: SuggestionSession['action'];
      actionLabel: string;
      originalText: string;
      suggestion: string;
      options: SuggestionSession['options'];
      catalog: ReturnType<typeof settingsCatalog>;
      busy: boolean;
      status?: string;
      error?: string;
    }
  | { type: 'busy'; busy: boolean; status?: string };

type WebToHost =
  | { type: 'ready' }
  | { type: 'accept'; suggestion: string; persistOptions: boolean; options: SuggestionSession['options'] }
  | { type: 'regenerate'; options: SuggestionSession['options']; persistOptions: boolean }
  | { type: 'discard' }
  | { type: 'openSettings' };

let panel: vscode.WebviewPanel | undefined;
let session: SuggestionSession | undefined;
let extContext: vscode.ExtensionContext | undefined;

export async function openSuggestionPanel(
  context: vscode.ExtensionContext,
  next: SuggestionSession,
): Promise<void> {
  extContext = context;
  session = next;

  if (panel) {
    panel.reveal(vscode.ViewColumn.Beside);
  } else {
    panel = vscode.window.createWebviewPanel(
      'lexaiSuggestion',
      'LexAI Suggestion',
      vscode.ViewColumn.Beside,
      { enableScripts: true, retainContextWhenHidden: true },
    );
    panel.webview.html = getHtml();
    panel.onDidDispose(() => {
      panel = undefined;
      session = undefined;
    });
    panel.webview.onDidReceiveMessage((msg: WebToHost) => {
      void handleMessage(msg);
    });
  }

  panel.title = `LexAI: ${actionLabel(next.action)}`;
  await postState();
}

async function postState(extra?: { status?: string; error?: string; busy?: boolean }): Promise<void> {
  if (!panel || !session) return;
  const payload: HostToWeb = {
    type: 'state',
    action: session.action,
    actionLabel: actionLabel(session.action),
    originalText: session.originalText,
    suggestion: session.suggestion,
    options: session.options,
    catalog: settingsCatalog(),
    busy: extra?.busy ?? false,
    status: extra?.status,
    error: extra?.error,
  };
  await panel.webview.postMessage(payload);
}

async function handleMessage(msg: WebToHost): Promise<void> {
  if (!panel || !session || !extContext) return;

  switch (msg.type) {
    case 'ready':
      await postState();
      break;
    case 'discard':
      panel.dispose();
      break;
    case 'openSettings':
      await vscode.commands.executeCommand('lexai.openSettings');
      break;
    case 'accept':
      await acceptSuggestion(msg.suggestion, msg.options, msg.persistOptions);
      break;
    case 'regenerate':
      await regenerate(msg.options, msg.persistOptions);
      break;
  }
}

async function maybePersistOptions(
  options: SuggestionSession['options'],
  persist: boolean,
): Promise<void> {
  if (!persist) return;
  await updateSettings({
    writingStyle: options.writingStyle,
    promptPattern: options.promptPattern,
    promptPersona: options.promptPersona,
    customPersona: options.customPersona,
    promptFormat: options.promptFormat,
    promptModel: options.promptModel,
  });
}

async function acceptSuggestion(
  suggestion: string,
  options: SuggestionSession['options'],
  persistOptions: boolean,
): Promise<void> {
  if (!session) return;
  await maybePersistOptions(options, persistOptions);
  const ok = await applySuggestionReplace(session, suggestion);
  if (!ok) {
    void vscode.window.showErrorMessage('LexAI: replace failed (editor busy or selection moved).');
    return;
  }
  void vscode.window.showInformationMessage(`LexAI: ${actionLabel(session.action)} applied.`);
  panel?.dispose();
}

async function regenerate(
  options: SuggestionSession['options'],
  persistOptions: boolean,
): Promise<void> {
  if (!session || !extContext || !panel) return;
  session.options = options;
  await maybePersistOptions(options, persistOptions);

  await panel.webview.postMessage({
    type: 'busy',
    busy: true,
    status: 'Generating another suggestion…',
  } satisfies HostToWeb);

  const resolved = await resolveConfig(extContext);
  if (resolved.error || !resolved.config) {
    await postState({ busy: false, error: resolved.error ?? 'configuration error' });
    return;
  }

  if (session.action === 'codeAssist') {
    const goal = session.instruction || 'Improve this code.';
    const ctx = session.contextText || session.originalText;
    const userPayload = [`### User instruction`, goal, '', ctx].join('\n');
    const response = await callProvider(resolved.config, userPayload, CODE_ASSIST_SYSTEM, {
      maxTokens: Math.max(2048, Math.min(8192, Math.ceil(session.originalText.length + 1500))),
    });
    if (response.error || !response.result) {
      await postState({
        busy: false,
        error: response.error ?? 'Empty response from provider.',
      });
      return;
    }
    session.suggestion = response.result.replace(/\r\n/g, '\n').trim()
      .replace(/^```(?:[\w.+-]+)?\n([\s\S]*?)\n```$/, '$1');
    await postState({ busy: false, status: 'New suggestion ready.' });
    return;
  }

  const response = await generateSuggestion({
    action: session.action as ActionId,
    text: session.originalText,
    config: resolved.config,
    writingStyle: options.writingStyle,
    promptParams: {
      pattern: options.promptPattern,
      persona: resolvePromptPersona(options.promptPersona, options.customPersona),
      format: options.promptFormat,
    },
    promptModel: options.promptModel || undefined,
  });

  if (response.error || !response.result) {
    await postState({
      busy: false,
      error: response.error ?? 'Empty response from provider.',
    });
    return;
  }

  session.suggestion = response.result;
  await postState({ busy: false, status: 'New suggestion ready.' });
}

function getHtml(): string {
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline';" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>LexAI Suggestion</title>
  <style>
    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: 16px 18px 28px;
    }
    h1 { font-size: 1.15rem; margin: 0 0 4px; }
    .sub { opacity: 0.75; margin-bottom: 12px; font-size: 0.85rem; }
    .toolbar {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 12px;
      position: sticky;
      top: 0;
      padding: 8px 0;
      background: var(--vscode-editor-background);
      z-index: 2;
    }
    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.danger {
      background: transparent;
      color: var(--vscode-errorForeground);
      border-color: var(--vscode-errorForeground);
    }
    button:disabled { opacity: 0.5; cursor: default; }
    .status { min-height: 1.2em; font-size: 0.85rem; margin-bottom: 10px; opacity: 0.85; }
    .status.error { color: var(--vscode-errorForeground); opacity: 1; }
    section {
      border: 1px solid var(--vscode-widget-border, rgba(128,128,128,0.35));
      border-radius: 8px;
      padding: 12px;
      margin-bottom: 12px;
    }
    section h2 {
      font-size: 0.72rem;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      margin: 0 0 8px;
      opacity: 0.75;
    }
    label { display: block; font-size: 0.8rem; margin-bottom: 4px; opacity: 0.9; }
    .field { margin-bottom: 10px; }
    .field:last-child { margin-bottom: 0; }
    select, textarea, input[type="text"] {
      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);
      font-family: var(--vscode-editor-font-family, monospace);
      font-size: 0.9rem;
    }
    textarea { min-height: 140px; resize: vertical; line-height: 1.4; }
    textarea.original { min-height: 90px; opacity: 0.9; }
    .hint { font-size: 0.78rem; opacity: 0.7; margin-top: 4px; }
    .check { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; margin-top: 8px; }
    .check input { width: auto; }
    #promptOptions { display: none; }
    #customPersonaWrap { display: none; }
    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
    @media (max-width: 520px) { .grid { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <h1 id="title">LexAI Suggestion</h1>
  <p class="sub">Review the suggestion. Accept to replace the selection, or regenerate with different options.</p>

  <div class="toolbar">
    <button type="button" id="accept">Accept</button>
    <button type="button" class="secondary" id="regenerate">Regenerate</button>
    <button type="button" class="danger" id="discard">Discard</button>
    <button type="button" class="secondary" id="openSettings">Full settings…</button>
  </div>
  <div class="status" id="status"></div>

  <section>
    <h2>Options for this suggestion</h2>
    <div class="grid">
      <div class="field" id="styleField">
        <label for="writingStyle">Writing style</label>
        <select id="writingStyle"></select>
      </div>
    </div>
    <div id="promptOptions">
      <div class="grid">
        <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" />
        </div>
        <div class="field">
          <label for="promptFormat">Format</label>
          <select id="promptFormat"></select>
        </div>
        <div class="field">
          <label for="promptModel">Prompt model override</label>
          <input id="promptModel" type="text" placeholder="(optional model id)" />
        </div>
      </div>
    </div>
    <label class="check">
      <input type="checkbox" id="persistOptions" />
      Also save these options as my defaults
    </label>
  </section>

  <section>
    <h2>Original selection</h2>
    <textarea class="original" id="original" readonly></textarea>
  </section>

  <section>
    <h2>Suggestion <span class="hint">(editable before Accept)</span></h2>
    <textarea id="suggestion"></textarea>
  </section>

  <script>
    const vscode = acquireVsCodeApi();
    const $ = (id) => document.getElementById(id);
    let catalog = null;
    let action = 'fix';
    let busy = false;

    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 syncPromptVisibility() {
      $('promptOptions').style.display = action === 'prompt' ? 'block' : 'none';
      $('styleField').style.display = action === 'prompt' ? 'none' : 'block';
      $('customPersonaWrap').style.display =
        $('promptPersona').value === 'Custom…' ? 'block' : 'none';
      const p = (catalog?.promptPatterns || []).find((x) => x.id === $('promptPattern').value);
      $('patternHint').textContent = p?.hint || '';
    }

    function setBusy(next, status) {
      busy = next;
      $('accept').disabled = next;
      $('regenerate').disabled = next;
      if (status !== undefined) {
        $('status').textContent = status || '';
        $('status').className = 'status';
      }
    }

    function collectOptions() {
      return {
        writingStyle: $('writingStyle').value,
        promptPattern: $('promptPattern').value,
        promptPersona: $('promptPersona').value,
        customPersona: $('customPersona').value,
        promptFormat: $('promptFormat').value,
        promptModel: $('promptModel').value.trim(),
      };
    }

    function applyState(msg) {
      catalog = msg.catalog;
      action = msg.action;
      $('title').textContent = 'LexAI: ' + msg.actionLabel;
      $('original').value = msg.originalText;
      $('suggestion').value = msg.suggestion;
      fillSelect($('writingStyle'), catalog.writingStyles, msg.options.writingStyle, (v) => ({ value: v, label: v }));
      fillSelect($('promptPattern'), catalog.promptPatterns, msg.options.promptPattern, (p) => ({
        value: p.id,
        label: (p.group ? p.group + ' — ' : '') + p.label,
      }));
      fillSelect($('promptPersona'), catalog.promptPersonas, msg.options.promptPersona, (v) => ({ value: v, label: v }));
      fillSelect($('promptFormat'), catalog.promptFormats, msg.options.promptFormat, (v) => ({ value: v, label: v }));
      $('customPersona').value = msg.options.customPersona || '';
      $('promptModel').value = msg.options.promptModel || '';
      syncPromptVisibility();
      setBusy(!!msg.busy, msg.status || '');
      if (msg.error) {
        $('status').textContent = msg.error;
        $('status').className = 'status error';
      }
    }

    window.addEventListener('message', (event) => {
      const msg = event.data;
      if (msg.type === 'state') applyState(msg);
      if (msg.type === 'busy') setBusy(msg.busy, msg.status);
    });

    $('promptPersona').addEventListener('change', syncPromptVisibility);
    $('promptPattern').addEventListener('change', syncPromptVisibility);

    $('accept').addEventListener('click', () => {
      vscode.postMessage({
        type: 'accept',
        suggestion: $('suggestion').value,
        options: collectOptions(),
        persistOptions: $('persistOptions').checked,
      });
    });
    $('regenerate').addEventListener('click', () => {
      vscode.postMessage({
        type: 'regenerate',
        options: collectOptions(),
        persistOptions: $('persistOptions').checked,
      });
    });
    $('discard').addEventListener('click', () => vscode.postMessage({ type: 'discard' }));
    $('openSettings').addEventListener('click', () => vscode.postMessage({ type: 'openSettings' }));

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