import * as vscode from "vscode";
import fs from "fs/promises";
import path from "path";
import { marked } from "marked";
import type { ViewPatchMessage } from "@codeflash/types";
import type { StructuredPatch } from "diff";
import { applyPatches, applyPatch, parsePatch } from "diff";
import type { Logger } from "../utils";
import type { GlobalState } from "../globalState";
import { GlobalStateKey } from "../globalState";
import { diff3Merge } from "../utils/diff.mjs";
import { captureException } from "../telemetry/sentry";
import { formatDuration } from "@codeflash/shared";
import { getStyleHeadTags } from "../utils/staticUris";
import { SIDEBAR_BUNDLE_DIR } from "../constants";
import {
  parseLineProfilerResults,
  getHeatLevel,
  type LineProfilerReport,
} from "../utils/lineProfilerParser";
import type { GitExtension } from "../types/git";
import { AUTH_TYPE } from "./AuthConfig";

const LINEBREAKS = /^.*(\r?\n|$)/gm;
const markerSize = 7;
const ourName = "codeflash";
const theirName = "yours";

interface Diff3Result {
  ok?: string[];
  conflict?: {
    a: string[];
    o: string[];
    b: string[];
  };
}

/**
 * Normalizes line endings to LF for consistent patch processing.
 */
function normalizeLineEndings(content: string): string {
  return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
}

export class GitPatchProvider {
  private patchFile: string;
  private functionName: string;
  private explanation: string;
  private speedup: string;
  private optimizationReview: string;
  private formattedOriginalRuntime: string;
  private formattedOptimizedRuntime: string;
  private originalLineProfiler: string;
  private optimizedLineProfiler: string;
  private merging: boolean = false;
  private _view: vscode.WebviewPanel | undefined;
  private _explanationPanel: vscode.WebviewPanel | undefined;
  private _contentProviderDisposables: vscode.Disposable[] = [];
  private onPatchApplied: () => void;
  private originalLineProfilerReport: LineProfilerReport;
  private optimizedLineProfilerReport: LineProfilerReport;

  constructor(
    private extensionUri: vscode.Uri,
    private globalState: GlobalState,
    private viewPayload: ViewPatchMessage["payload"],
    private logger: Logger,
    _onPatchApplied?: () => void,
  ) {
    this.patchFile = viewPayload.patchFile;
    this.speedup = viewPayload.speedupStr || "";
    this.functionName = viewPayload.functionName;
    this.explanation = viewPayload.explanation ?? "";
    this.optimizationReview = viewPayload.optimizationReview ?? "";
    this.formattedOriginalRuntime = formatDuration(viewPayload.originalRuntime);
    this.formattedOptimizedRuntime = formatDuration(
      viewPayload.optimizedRuntime,
    );
    this.originalLineProfiler = viewPayload.originalLineProfiler ?? "";
    this.optimizedLineProfiler = viewPayload.optimizedLineProfiler ?? "";
    this.originalLineProfilerReport = parseLineProfilerResults(
      this.originalLineProfiler,
    );
    this.optimizedLineProfilerReport = parseLineProfilerResults(
      this.optimizedLineProfiler,
    );
    // Silence unused warning for WIP method
    void this.generateLineProfilerHtml;
    this.onPatchApplied =
      _onPatchApplied ??
      function () {
        return Promise.resolve();
      };
  }

  /**
   * Shows the optimization patch using the native diff editor.
   * Falls back to webview if native diff fails.
   */
  public async showPatch(): Promise<void> {
    // Always use native diff editor for better IDE integration
    // The webview fallback is handled in showNativeDiff's error handler
    await this.showNativeDiff();
  }

  /**
   * Gets the original file content from disk or context files.
   * Returns undefined if the file cannot be found.
   */
  private async getOriginalFileContent(
    targetFile: string,
  ): Promise<string | undefined> {
    const filePath = path.join(this.workspaceRoot, targetFile);

    try {
      const content = await fs.readFile(filePath, "utf-8");
      return normalizeLineEndings(content);
    } catch {
      // If file doesn't exist on disk, try to get it from context files
      const contextEntries = this.globalState.get(
        GlobalStateKey.ContextFiles,
        {},
      )![this.viewPayload.id];

      if (contextEntries) {
        for (const [worktreePath, content] of Object.entries(contextEntries)) {
          if (worktreePath.includes(targetFile)) {
            return normalizeLineEndings(content);
          }
        }
      }

      return undefined;
    }
  }

  /**
   * Shows the optimization diff using VSCode's native diff editor.
   * This provides better IDE integration and a familiar diff experience.
   * Supports multi-file patches by opening separate diff editors for each file.
   */
  private async showNativeDiff(): Promise<void> {
    try {
      this.logger.info(
        `Showing native diff editor for optimization: ${this.functionName}`,
      );

      // Parse the patch file to extract original and optimized content
      let patchContent = await fs.readFile(this.patchFile, "utf8");
      patchContent = normalizeLineEndings(patchContent);

      const patches = parsePatch(patchContent);

      if (!patches || patches.length === 0) {
        throw new Error("Failed to parse patch file");
      }

      this.logger.info(
        `Processing ${patches.length} file(s) in optimization patch`,
      );

      // Store content providers for all files
      const fileContents: Map<string, { original: string; optimized: string }> =
        new Map();

      // Process each patch (file) in the multi-file patch
      for (const patch of patches) {
        const targetFile = this.removeFilesPrefixes(patch.oldFileName);

        // Get the original file content
        const originalContent = await this.getOriginalFileContent(targetFile);

        if (!originalContent) {
          this.logger.error(
            `Could not find original file content for ${targetFile}`,
          );
          continue; // Skip this file but continue with others
        }

        // Apply the patch to get the optimized content
        const optimizedContent = applyPatch(originalContent, patch, {
          fuzzFactor: 99,
        });

        if (!optimizedContent) {
          this.logger.error(`Failed to apply patch for ${targetFile}`);
          continue; // Skip this file but continue with others
        }

        fileContents.set(targetFile, {
          original: originalContent,
          optimized: optimizedContent,
        });
      }

      if (fileContents.size === 0) {
        throw new Error(
          "Failed to apply patch to any files in the optimization",
        );
      }

      this.logger.info(
        `Successfully processed ${fileContents.size}/${patches.length} files`,
      );

      // Register content providers for all files
      // We use a single provider that can handle multiple files based on the URI
      const allOriginalContents = new Map<string, string>();
      const allOptimizedContents = new Map<string, string>();

      for (const [targetFile, contents] of fileContents) {
        allOriginalContents.set(targetFile, contents.original);
        allOptimizedContents.set(targetFile, contents.optimized);
      }

      const originalProvider = new (class
        implements vscode.TextDocumentContentProvider
      {
        provideTextDocumentContent(uri: vscode.Uri): string {
          const rawPath = uri.path;
          const filePath = rawPath.startsWith("/") ? rawPath.slice(1) : rawPath;
          return allOriginalContents.get(filePath) || "";
        }
      })();

      const optimizedProvider = new (class
        implements vscode.TextDocumentContentProvider
      {
        provideTextDocumentContent(uri: vscode.Uri): string {
          const rawPath = uri.path;
          const filePath = rawPath.startsWith("/") ? rawPath.slice(1) : rawPath;
          return allOptimizedContents.get(filePath) || "";
        }
      })();

      // Dispose previous providers before registering new ones
      this._contentProviderDisposables.forEach((d) => d.dispose());
      this._contentProviderDisposables = [];

      this._contentProviderDisposables.push(
        vscode.workspace.registerTextDocumentContentProvider(
          "codeflash-original",
          originalProvider,
        ),
        vscode.workspace.registerTextDocumentContentProvider(
          "codeflash-optimized",
          optimizedProvider,
        ),
      );

      // Open diff editors for all files
      // For multiple files, we open them in sequence so user can see all changes
      const fileList = Array.from(fileContents.keys());
      for (let i = 0; i < fileList.length; i++) {
        const targetFile = fileList[i]!;

        const originalUri = vscode.Uri.parse(
          `codeflash-original:${targetFile}?${this.viewPayload.id}`,
        );
        const optimizedUri = vscode.Uri.parse(
          `codeflash-optimized:${targetFile}?${this.viewPayload.id}`,
        );

        const diffTitle =
          fileList.length > 1
            ? `${path.basename(targetFile)} (${i + 1}/${fileList.length}) - Original ↔ Optimized`
            : `${path.basename(targetFile)} (Original ↔ Optimized)`;

        await vscode.commands.executeCommand(
          "vscode.diff",
          originalUri,
          optimizedUri,
          diffTitle,
          // Important: avoid "preview" so opening multiple diffs doesn't replace the previous tab
          { preview: false },
        );
      }

      // Log summary for user
      if (fileList.length > 1) {
        this.logger.info(
          `Opened ${fileList.length} diff editors for multi-file optimization: ${fileList.join(", ")}`,
        );
      }

      // Always show explanation panel with action buttons
      await this.showExplanationPanel();

      this.logger.info(
        `Native diff editor opened successfully for ${this.functionName}`,
      );
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      this.logger.error(
        `Failed to show native diff: ${errorMessage}`,
        error instanceof Error ? error : undefined,
      );
      vscode.window.showErrorMessage(
        `Failed to open diff view: ${errorMessage}`,
      );

      // Fallback to webview on error
      try {
        const patchContent = await fs.readFile(this.patchFile, "utf8");
        await this.showPatchWebview(
          patchContent,
          `Codeflash: ${this.functionName} (${this.speedup})`,
        );
      } catch (fallbackError) {
        this.logger.error(
          `Failed to fallback to webview: ${fallbackError}`,
          fallbackError instanceof Error ? fallbackError : undefined,
        );
      }
    }
  }

  /**
   * Shows the optimization explanation in a separate webview panel.
   */
  private async showExplanationPanel(): Promise<void> {
    if (this._explanationPanel) {
      this._explanationPanel.reveal(vscode.ViewColumn.Two);
      return;
    }

    const panel = vscode.window.createWebviewPanel(
      "codeflashExplanation",
      `Optimization Explanation: ${this.functionName}`,
      vscode.ViewColumn.Two,
      {
        enableScripts: true,
        retainContextWhenHidden: true,
        localResourceRoots: [
          vscode.Uri.joinPath(this.extensionUri, "media"),
          vscode.Uri.joinPath(this.extensionUri, SIDEBAR_BUNDLE_DIR, "assets"),
        ],
      },
    );

    panel.iconPath = vscode.Uri.joinPath(
      this.extensionUri,
      "media",
      "codeflash.svg",
    );

    const explanationMarkdown = await marked(this.explanation);
    const extraStyle = getStyleHeadTags(panel.webview, this.extensionUri);

    panel.webview.html = `
      <!DOCTYPE html>
      <html>
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        ${extraStyle}
        <style>
          body {
            font-family: var(--vscode-font-family);
            font-size: var(--vscode-font-size);
            color: var(--vscode-editor-foreground);
            background-color: var(--vscode-editor-background);
            padding: 20px;
            line-height: 1.6;
          }
          h1, h2, h3 {
            color: var(--vscode-textLink-foreground);
          }
          code {
            background-color: var(--vscode-textCodeBlock-background);
            padding: 2px 4px;
            border-radius: 3px;
          }
          pre {
            background-color: var(--vscode-textCodeBlock-background);
            padding: 12px;
            border-radius: 4px;
            overflow-x: auto;
          }
          .metadata {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
            margin-bottom: 20px;
            padding: 12px;
            background-color: var(--vscode-editor-lineHighlightBackground);
            border-radius: 4px;
          }
          .badge {
            display: inline-flex;
            align-items: center;
            gap: 4px;
            padding: 4px 8px;
            background-color: var(--vscode-badge-background);
            color: var(--vscode-badge-foreground);
            border-radius: 4px;
            font-size: 12px;
          }
          .action-buttons {
            display: flex;
            gap: 12px;
            margin-top: 20px;
            padding-top: 20px;
            border-top: 1px solid var(--vscode-panel-border);
          }
          .action-button {
            flex: 1;
            padding: 10px 16px;
            border: none;
            border-radius: 4px;
            font-size: 13px;
            font-weight: 600;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
            transition: all 0.2s ease;
          }
          .action-button-primary {
            background-color: var(--vscode-button-background, #0e639c);
            color: var(--vscode-button-foreground, #ffffff);
          }
          .action-button-primary:hover {
            background-color: var(--vscode-button-hoverBackground, #1177bb);
          }
          .action-button-secondary {
            background-color: var(--vscode-button-secondaryBackground, #3c3c3c);
            color: var(--vscode-button-secondaryForeground, #ffffff);
          }
          .action-button-secondary:hover {
            background-color: var(--vscode-button-secondaryHoverBackground, #4c4c4c);
          }

          /* Line Profiler Styles */
          .lp-section {
            margin-top: 24px;
            border: 1px solid var(--vscode-panel-border);
            border-radius: 8px;
            overflow: hidden;
          }

          .lp-section-header {
            display: flex;
            align-items: center;
            gap: 10px;
            padding: 14px 18px;
            background: var(--vscode-sideBar-background);
            cursor: pointer;
            user-select: none;
            border-bottom: 1px solid var(--vscode-panel-border);
          }

          .lp-section-header:hover {
            background: var(--vscode-list-hoverBackground);
          }

          .lp-section-icon { font-size: 18px; }
          .lp-section-title { font-size: 14px; font-weight: 600; flex: 1; }
          .lp-toggle-icon { font-size: 12px; color: var(--vscode-descriptionForeground); transition: transform 0.2s ease; }

          .lp-section-content { padding: 16px; }

          .lp-comparison {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
          }

          @media (max-width: 1200px) {
            .lp-comparison { grid-template-columns: 1fr; }
          }

          .lp-panel {
            border: 1px solid var(--vscode-panel-border);
            border-radius: 6px;
            overflow: hidden;
          }

          .lp-panel-header {
            padding: 10px 14px;
            background: var(--vscode-sideBar-background);
            border-bottom: 1px solid var(--vscode-panel-border);
          }

          .lp-panel-title { font-weight: 600; font-size: 13px; }

          .lp-function-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 12px;
            background: var(--vscode-textBlockQuote-background);
            margin: 12px;
            border-radius: 4px;
          }

          .lp-function-name {
            font-weight: 600;
            font-size: 12px;
            color: var(--vscode-symbolIcon-functionForeground);
            font-family: var(--vscode-editor-font-family, monospace);
          }

          .lp-function-time { font-size: 11px; color: var(--vscode-descriptionForeground); }

          .lp-table {
            width: 100%;
            border-collapse: collapse;
            font-size: 12px;
            font-family: var(--vscode-editor-font-family, monospace);
          }

          .lp-th {
            text-align: left;
            padding: 8px 10px;
            font-weight: 600;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            color: var(--vscode-descriptionForeground);
            background: var(--vscode-editor-lineHighlightBackground);
            border-bottom: 1px solid var(--vscode-panel-border);
          }

          .lp-th-code { width: 50%; }
          .lp-row { transition: background-color 0.15s ease; }
          .lp-row:hover { background-color: var(--vscode-list-hoverBackground) !important; }

          .lp-cell {
            padding: 6px 10px;
            border-bottom: 1px solid var(--vscode-panel-border);
            vertical-align: middle;
          }

          .lp-hits, .lp-time, .lp-perhit {
            text-align: right;
            color: var(--vscode-descriptionForeground);
            white-space: nowrap;
          }

          .lp-percent { width: 100px; min-width: 100px; }

          .lp-percent-container {
            position: relative;
            height: 18px;
            background: rgba(127, 127, 127, 0.1);
            border-radius: 3px;
            overflow: hidden;
          }

          .lp-percent-bar {
            position: absolute;
            top: 0;
            left: 0;
            height: 100%;
            border-radius: 3px;
            transition: width 0.3s ease;
          }

          .lp-percent-text {
            position: absolute;
            right: 6px;
            top: 50%;
            transform: translateY(-50%);
            font-size: 10px;
            font-weight: 600;
          }

          .lp-code {
            font-family: var(--vscode-editor-font-family, monospace);
            font-size: 12px;
            white-space: pre;
            overflow-x: auto;
          }

          .lp-no-data {
            padding: 24px;
            text-align: center;
            color: var(--vscode-descriptionForeground);
            font-style: italic;
          }

          /* Heat map colors */
          .lp-row.hot-4 { background: rgba(220, 38, 38, 0.25); }
          .lp-row.hot-4 .lp-percent-bar { background: linear-gradient(90deg, rgba(220, 38, 38, 0.8), rgba(185, 28, 28, 0.9)); }

          .lp-row.hot-3 { background: rgba(234, 88, 12, 0.2); }
          .lp-row.hot-3 .lp-percent-bar { background: linear-gradient(90deg, rgba(234, 88, 12, 0.7), rgba(220, 38, 38, 0.7)); }

          .lp-row.hot-2 { background: rgba(245, 158, 11, 0.15); }
          .lp-row.hot-2 .lp-percent-bar { background: linear-gradient(90deg, rgba(245, 158, 11, 0.7), rgba(234, 88, 12, 0.6)); }

          .lp-row.hot-1 { background: rgba(250, 204, 21, 0.1); }
          .lp-row.hot-1 .lp-percent-bar { background: linear-gradient(90deg, rgba(250, 204, 21, 0.6), rgba(245, 158, 11, 0.5)); }

          .lp-row.cold { background: rgba(34, 197, 94, 0.08); }
          .lp-row.cold .lp-percent-bar { background: linear-gradient(90deg, rgba(34, 197, 94, 0.5), rgba(74, 222, 128, 0.4)); }

          /* Toggle buttons */
          .lp-view-toggle {
            display: flex;
            gap: 4px;
            margin-left: auto;
          }

          .lp-view-btn {
            background: var(--vscode-button-secondaryBackground);
            color: var(--vscode-button-secondaryForeground);
            border: none;
            padding: 4px 10px;
            border-radius: 4px;
            font-size: 11px;
            cursor: pointer;
            transition: all 0.15s;
          }

          .lp-view-btn:hover {
            background: var(--vscode-button-secondaryHoverBackground);
          }

          .lp-view-btn.active {
            background: var(--vscode-button-background);
            color: var(--vscode-button-foreground);
          }

          /* Split/Unified view containers */
          .lp-split-view { display: block; }
          .lp-split-view.hidden { display: none; }
          .lp-unified-view { display: none; }
          .lp-unified-view.active { display: block; }

          /* Diff row styles */
          .lp-row.row-del { background: rgba(248, 81, 73, 0.15) !important; }
          .lp-row.row-add { background: rgba(46, 160, 67, 0.15) !important; }
          .diff-marker { display: inline-block; width: 14px; font-weight: 600; margin-right: 4px; }
          .diff-del { color: #f85149; }
          .diff-add { color: #3fb950; }
        </style>
      </head>
      <body>
        <div class="metadata">
          <span class="badge">⚡ ${this.functionName}</span>
          <span class="badge">⏱️ ${this.formattedOriginalRuntime} → ${this.formattedOptimizedRuntime}</span>
          <span class="badge">🚀 ${this.speedup}</span>
          ${this.optimizationReview ? `<span class="badge">🎯 Quality: ${this.optimizationReview}</span>` : ""}
        </div>
        <div class="explanation-content">
          ${explanationMarkdown}
        </div>

        <!-- Line Profiler Section -->
        ${
          this.originalLineProfiler || this.optimizedLineProfiler
            ? `
        <div class="lp-section">
          <div class="lp-section-header">
            <span class="lp-section-icon" onclick="toggleLineProfiler()">📊</span>
            <span class="lp-section-title" onclick="toggleLineProfiler()">Performance Profile Comparison</span>
            <div class="lp-view-toggle">
              <button class="lp-view-btn active" id="btn-split" onclick="event.stopPropagation(); setLpView('split')">Split</button>
              <button class="lp-view-btn" id="btn-unified" onclick="event.stopPropagation(); setLpView('unified')">Unified</button>
            </div>
            <span class="lp-toggle-icon" id="lp-toggle-icon" onclick="toggleLineProfiler()">▼</span>
          </div>
          <div class="lp-section-content" id="lp-content">
            <!-- Split View -->
            <div class="lp-split-view" id="lp-split-view">
              <div class="lp-comparison">
                <!-- Original Panel -->
                <div class="lp-panel">
                  <div class="lp-panel-header">
                    <span class="lp-panel-title">Original</span>
                  </div>
                  <div class="lp-function-header">
                    <span class="lp-function-name">${this.functionName}</span>
                    <span class="lp-function-time">Total Time: ${this.formattedOriginalRuntime}</span>
                  </div>
                  <table class="lp-table">
                    <thead>
                      <tr>
                        <th class="lp-th">Hits</th>
                        <th class="lp-th">Time</th>
                        <th class="lp-th">Per Hit</th>
                        <th class="lp-th">% Time</th>
                        <th class="lp-th lp-th-code">Line Contents</th>
                      </tr>
                    </thead>
                    <tbody id="lp-original-tbody"></tbody>
                  </table>
                </div>

                <!-- Optimized Panel -->
                <div class="lp-panel">
                  <div class="lp-panel-header">
                    <span class="lp-panel-title">Optimized</span>
                  </div>
                  <div class="lp-function-header">
                    <span class="lp-function-name">${this.functionName}</span>
                    <span class="lp-function-time">Total Time: ${this.formattedOptimizedRuntime}</span>
                  </div>
                  <table class="lp-table">
                    <thead>
                      <tr>
                        <th class="lp-th">Hits</th>
                        <th class="lp-th">Time</th>
                        <th class="lp-th">Per Hit</th>
                        <th class="lp-th">% Time</th>
                        <th class="lp-th lp-th-code">Line Contents</th>
                      </tr>
                    </thead>
                    <tbody id="lp-optimized-tbody"></tbody>
                  </table>
                </div>
              </div>
            </div>

            <!-- Unified View -->
            <div class="lp-unified-view" id="lp-unified-view">
              <div class="lp-panel">
                <div class="lp-panel-header">
                  <span class="lp-panel-title">Unified Diff</span>
                </div>
                <div class="lp-function-header">
                  <span class="lp-function-name">${this.functionName}</span>
                  <span class="lp-function-time">Original: ${this.formattedOriginalRuntime} → Optimized: ${this.formattedOptimizedRuntime}</span>
                </div>
                <table class="lp-table">
                  <thead>
                    <tr>
                      <th class="lp-th">Hits</th>
                      <th class="lp-th">Time</th>
                      <th class="lp-th">Per Hit</th>
                      <th class="lp-th">% Time</th>
                      <th class="lp-th lp-th-code">Line Contents</th>
                    </tr>
                  </thead>
                  <tbody id="lp-unified-tbody"></tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
        `
            : ""
        }

        <div class="action-buttons">
          <button class="action-button action-button-primary" id="apply-btn">
            <span class="codicon codicon-check"></span>
            Apply Optimization
          </button>
          <button class="action-button action-button-secondary" id="create-pr-btn">
            <span class="codicon codicon-git-pull-request"></span>
            Create Pull Request
          </button>
        </div>
        <script>
          const vscode = acquireVsCodeApi();

          // Line profiler data
          const lpOriginalData = ${JSON.stringify(this.originalLineProfiler || "")};
          const lpOptimizedData = ${JSON.stringify(this.optimizedLineProfiler || "")};

          function toggleLineProfiler() {
            const content = document.getElementById('lp-content');
            const icon = document.getElementById('lp-toggle-icon');
            if (content.style.display === 'none') {
              content.style.display = 'block';
              icon.textContent = '▼';
            } else {
              content.style.display = 'none';
              icon.textContent = '▶';
            }
          }

          function parseProfilerData(markdown) {
            if (!markdown) return { rows: [], unit: 1e-6 };
            const lines = markdown.split('\\n');
            const rows = [];
            let inTable = false;
            let unit = 1e-6; // Default to microseconds

            for (const line of lines) {
              // Extract timer unit from header: "# Timer unit: 1e-06 s"
              if (line.startsWith('# Timer unit:')) {
                const match = line.match(/Timer unit:\\s*([\\d.eE+-]+)/);
                if (match) {
                  unit = parseFloat(match[1]);
                }
                continue;
              }
              if (line.startsWith('|') && line.includes('Hits')) {
                inTable = true;
                continue;
              }
              if (line.startsWith('|--') || line.startsWith('| --')) continue;
              if (inTable && line.startsWith('|')) {
                const cells = line.split('|').map(c => c.trim()).filter(c => c);
                if (cells.length >= 5) {
                  rows.push({
                    hits: cells[0] || '',
                    time: cells[1] || '',
                    perHit: cells[2] || '',
                    percent: cells[3] || '',
                    code: cells.slice(4).join('|'),
                    percentNum: parseFloat(cells[3]) || 0
                  });
                }
              }
            }
            return { rows, unit };
          }

          function getHeatClass(percent) {
            if (percent > 50) return 'hot-4';
            if (percent > 30) return 'hot-3';
            if (percent > 15) return 'hot-2';
            if (percent > 5) return 'hot-1';
            return 'cold';
          }

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

          function formatTime(timeStr, unit) {
            if (!timeStr || timeStr.trim() === '') return '';
            const str = timeStr.trim();

            // Try to extract number - handles "1.23e-05", "0.000123", "123.45"
            const rawNum = parseFloat(str);
            if (isNaN(rawNum)) return str;

            // Convert from timer units to seconds
            // unit is typically 1e-6 (microseconds), so num * unit = seconds
            const num = rawNum * (unit || 1e-6);

            // Format to human readable
            if (num >= 3600) {
              const hrs = Math.floor(num / 3600);
              const mins = Math.floor((num % 3600) / 60);
              const secs = (num % 60).toFixed(1);
              return hrs + 'h ' + mins + 'm ' + secs + 's';
            } else if (num >= 60) {
              const mins = Math.floor(num / 60);
              const secs = (num % 60).toFixed(1);
              return mins + 'm ' + secs + 's';
            } else if (num >= 1) {
              return num.toFixed(2) + 's';
            } else if (num >= 0.001) {
              return (num * 1000).toFixed(2) + 'ms';
            } else if (num >= 0.000001) {
              return (num * 1000000).toFixed(2) + 'μs';
            } else if (num > 0) {
              return (num * 1000000000).toFixed(2) + 'ns';
            }
            return '0s';
          }

          function renderProfilerTable(rows, tbodyId, unit) {
            const tbody = document.getElementById(tbodyId);
            if (!tbody) return;
            if (!rows.length) {
              tbody.innerHTML = '<tr><td colspan="5" class="lp-no-data">No profiler data available</td></tr>';
              return;
            }

            let html = '';
            rows.forEach(row => {
              const heatClass = getHeatClass(row.percentNum);
              const barWidth = Math.min(row.percentNum, 100);
              const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

              html += '<tr class="lp-row ' + heatClass + '">';
              html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
              html += '<td class="lp-cell lp-time">' + formatTime(row.time, unit) + '</td>';
              html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, unit) + '</td>';
              html += '<td class="lp-cell lp-percent">';
              html += '<div class="lp-percent-container">';
              html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
              html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
              html += '</div></td>';
              html += '<td class="lp-cell lp-code"><code>' + escapeHtml(row.code) + '</code></td>';
              html += '</tr>';
            });

            tbody.innerHTML = html;
          }

          function renderUnifiedTable(originalRows, optimizedRows, origUnit, optUnit) {
            const tbody = document.getElementById('lp-unified-tbody');
            if (!tbody) return;
            if (!originalRows.length && !optimizedRows.length) {
              tbody.innerHTML = '<tr><td colspan="5" class="lp-no-data">No profiler data available</td></tr>';
              return;
            }

            let html = '';

            // Original lines with heat colors
            originalRows.forEach(row => {
              const heatClass = getHeatClass(row.percentNum);
              const barWidth = Math.min(row.percentNum, 100);
              const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

              html += '<tr class="lp-row ' + heatClass + '">';
              html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
              html += '<td class="lp-cell lp-time">' + formatTime(row.time, origUnit) + '</td>';
              html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, origUnit) + '</td>';
              html += '<td class="lp-cell lp-percent">';
              html += '<div class="lp-percent-container">';
              html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
              html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
              html += '</div></td>';
              html += '<td class="lp-cell lp-code"><code><span class="diff-marker diff-del">-</span>' + escapeHtml(row.code) + '</code></td>';
              html += '</tr>';
            });

            // Optimized lines with heat colors
            optimizedRows.forEach(row => {
              const heatClass = getHeatClass(row.percentNum);
              const barWidth = Math.min(row.percentNum, 100);
              const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

              html += '<tr class="lp-row ' + heatClass + '">';
              html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
              html += '<td class="lp-cell lp-time">' + formatTime(row.time, optUnit) + '</td>';
              html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, optUnit) + '</td>';
              html += '<td class="lp-cell lp-percent">';
              html += '<div class="lp-percent-container">';
              html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
              html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
              html += '</div></td>';
              html += '<td class="lp-cell lp-code"><code><span class="diff-marker diff-add">+</span>' + escapeHtml(row.code) + '</code></td>';
              html += '</tr>';
            });

            tbody.innerHTML = html;
          }

          function setLpView(view) {
            const splitView = document.getElementById('lp-split-view');
            const unifiedView = document.getElementById('lp-unified-view');
            const btnSplit = document.getElementById('btn-split');
            const btnUnified = document.getElementById('btn-unified');

            if (view === 'split') {
              splitView.classList.remove('hidden');
              unifiedView.classList.remove('active');
              btnSplit.classList.add('active');
              btnUnified.classList.remove('active');
            } else {
              splitView.classList.add('hidden');
              unifiedView.classList.add('active');
              btnSplit.classList.remove('active');
              btnUnified.classList.add('active');
            }
          }

          document.addEventListener('DOMContentLoaded', function() {
            // Render line profiler tables
            const originalData = parseProfilerData(lpOriginalData);
            const optimizedData = parseProfilerData(lpOptimizedData);
            renderProfilerTable(originalData.rows, 'lp-original-tbody', originalData.unit);
            renderProfilerTable(optimizedData.rows, 'lp-optimized-tbody', optimizedData.unit);
            renderUnifiedTable(originalData.rows, optimizedData.rows, originalData.unit, optimizedData.unit);
          });

          document.getElementById('apply-btn').addEventListener('click', () => {
            vscode.postMessage({ command: 'applyPatch' });
          });

          document.getElementById('create-pr-btn').addEventListener('click', () => {
            vscode.postMessage({ command: 'createPR' });
          });
        </script>
      </body>
      </html>
    `;

    // Handle messages from the explanation panel
    panel.webview.onDidReceiveMessage((message) => {
      this.handleExplanationPanelMessage(message);
    });

    panel.onDidDispose(() => {
      this._explanationPanel = undefined;
    });

    this._explanationPanel = panel;
  }

  /**
   * Handles messages from the explanation panel webview.
   */
  private handleExplanationPanelMessage(message: { command: string }): void {
    switch (message.command) {
      case "applyPatch":
        this.applyPatch();
        break;
      case "createPR":
        this.createPullRequest();
        break;
      default:
        this.logger.warn(
          `Unknown explanation panel message command: ${message.command}`,
        );
    }
  }

  /**
   * Creates a pull request using the Codeflash API.
   * Uses the same API endpoint as the webapp: /cfapi/create-pr
   */
  private async createPullRequest(): Promise<void> {
    try {
      this.logger.info(
        `[createPR] Starting PR creation for optimization: ${this.functionName}`,
      );

      // Get git repository info
      this.logger.info("[createPR] Getting git extension...");
      const gitExtension =
        vscode.extensions.getExtension<GitExtension>("vscode.git")?.exports;
      if (!gitExtension) {
        throw new Error("Git extension not found");
      }

      const gitApi = gitExtension.getAPI(1);
      this.logger.info(
        `[createPR] Found ${gitApi.repositories.length} git repositories`,
      );
      if (gitApi.repositories.length === 0) {
        throw new Error("No git repository found");
      }

      const repository = gitApi.repositories[0]!;
      const remotes = repository.state.remotes;
      this.logger.info(
        `[createPR] Found ${remotes.length} remotes: ${remotes.map((r) => r.name).join(", ")}`,
      );

      // Find origin remote
      const originRemote = remotes.find((r) => r.name === "origin");
      if (!originRemote) {
        throw new Error("No origin remote found");
      }

      // Parse owner and repo from remote URL
      // Example: https://github.com/owner/repo.git or git@github.com:owner/repo.git
      const remoteUrl = originRemote.fetchUrl || originRemote.pushUrl || "";
      this.logger.info(`[createPR] Remote URL: ${remoteUrl}`);
      const match = remoteUrl.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
      if (!match) {
        throw new Error(
          `Could not parse GitHub owner/repo from remote URL: ${remoteUrl}`,
        );
      }

      const owner = match[1]!;
      const repo = match[2]!;
      const baseBranch = repository.state.HEAD?.name || "main";

      this.logger.info(
        `[createPR] Repository: ${owner}/${repo}, base branch: ${baseBranch}`,
      );

      // Parse the patch file to get file changes
      this.logger.info(`[createPR] Reading patch file: ${this.patchFile}`);
      let patchContent = await fs.readFile(this.patchFile, "utf8");
      patchContent = normalizeLineEndings(patchContent);
      const patches = parsePatch(patchContent);

      if (!patches || patches.length === 0) {
        throw new Error("Failed to parse patch file");
      }
      this.logger.info(`[createPR] Parsed ${patches.length} patches from file`);

      // Build file changes in the format expected by the API
      // Format matches webapp: { [filePath]: { oldContent, newContent } }
      const fileChanges: Record<
        string,
        { oldContent: string; newContent: string }
      > = {};

      for (const patch of patches) {
        const targetFile = this.removeFilesPrefixes(patch.oldFileName);
        this.logger.info(`[createPR] Processing file: ${targetFile}`);
        const originalContent = await this.getOriginalFileContent(targetFile);

        if (!originalContent) {
          this.logger.warn(
            `[createPR] Skipping file ${targetFile} - could not find original content`,
          );
          continue;
        }

        const optimizedContent = applyPatch(originalContent, patch, {
          fuzzFactor: 99,
        });

        if (!optimizedContent) {
          this.logger.warn(
            `[createPR] Skipping file ${targetFile} - failed to apply patch`,
          );
          continue;
        }

        fileChanges[targetFile] = {
          oldContent: originalContent,
          newContent: optimizedContent,
        };
        this.logger.info(
          `[createPR] Added file ${targetFile} to changes (old: ${originalContent.length} chars, new: ${optimizedContent.length} chars)`,
        );
      }

      if (Object.keys(fileChanges).length === 0) {
        throw new Error("No file changes to create PR with");
      }

      // Get the first file path for PR metadata
      const firstFilePath = Object.keys(fileChanges)[0]!;
      this.logger.info(
        `[createPR] Total files to include in PR: ${Object.keys(fileChanges).length}`,
      );

      // Get access token - the API key is stored by the LSP when user logs in
      // We need to get a fresh session since CodeflashAuthProvider.getSessions() always returns []
      // The session's accessToken IS the Codeflash API key
      this.logger.info(
        `[createPR] Getting authentication session with AUTH_TYPE: ${AUTH_TYPE}`,
      );

      // Always use createIfNone: true since sessions are not persisted in the auth provider
      // This will either:
      // 1. Create a new session if user isn't logged in (opens browser)
      // 2. Create a new session using cached credentials if available
      let session: vscode.AuthenticationSession | undefined;

      try {
        session = await vscode.authentication.getSession(AUTH_TYPE, [], {
          createIfNone: true,
        });
        this.logger.info(
          `[createPR] Got session: ${session ? `account=${session.account.label}` : "none"}`,
        );
      } catch (authError) {
        const errorMessage =
          authError instanceof Error ? authError.message : String(authError);

        // Handle user cancellation gracefully
        if (
          errorMessage.includes("User cancelled") ||
          errorMessage.includes("Cancelled")
        ) {
          this.logger.info("[createPR] Authentication cancelled by user");
          return;
        }

        this.logger.error(
          `[createPR] Error getting session: ${authError}`,
          authError instanceof Error ? authError : undefined,
        );
        throw new Error(`Authentication failed: ${errorMessage}`);
      }

      if (!session) {
        this.logger.error(
          "[createPR] No session returned after authentication",
        );
        vscode.window.showErrorMessage(
          "Failed to authenticate. Please try again.",
        );
        return;
      }

      this.logger.info(
        `[createPR] Using session for account: ${session.account.label}`,
      );

      // Show progress while creating PR
      await vscode.window.withProgress(
        {
          location: vscode.ProgressLocation.Notification,
          title: "Creating Pull Request",
          cancellable: false,
        },
        async (progress) => {
          progress.report({ message: "Preparing PR..." });

          // Call the Codeflash API - same endpoint as Python CLI uses
          // Production URL is https://app.codeflash.ai/cfapi/create-pr
          const apiUrl =
            process.env.CODEFLASH_CFAPI_URL || "https://app.codeflash.ai";
          const endpoint = `${apiUrl}/cfapi/create-pr`;

          this.logger.info(`[createPR] Calling API: ${endpoint}`);

          // Build request body matching Python CLI format (cfapi.py create_pr / PrComment.to_json)
          const requestBody = {
            owner,
            repo,
            baseBranch,
            diffContents: fileChanges,
            prCommentFields: {
              function_name: this.functionName,
              optimization_explanation: this.explanation,
              file_path: firstFilePath, // Python CLI uses 'file_path', not 'relative_file_path'
              speedup_x: this.speedup,
              speedup_pct: this.speedup, // Also include speedup_pct
              best_runtime: this.formattedOptimizedRuntime,
              original_runtime: this.formattedOriginalRuntime,
              loop_count: 1, // Default loop count
              report_table: {}, // Empty report table
            },
            existingTests: "",
            generatedTests: "",
            traceId: this.viewPayload.id,
            coverage_message: "",
            replayTests: "",
            concolicTests: "",
            optimizationReview: this.optimizationReview,
            // Line profiler data at root level (not inside prCommentFields)
            originalLineProfiler: this.originalLineProfiler,
            optimizedLineProfiler: this.optimizedLineProfiler,
          };

          this.logger.info(
            `[createPR] Request body: traceId=${requestBody.traceId}, owner=${owner}, repo=${repo}, baseBranch=${baseBranch}, files=${Object.keys(fileChanges).join(", ")}`,
          );
          this.logger.info(
            `[createPR] prCommentFields: function_name=${requestBody.prCommentFields.function_name}, ` +
              `speedup_x=${requestBody.prCommentFields.speedup_x}, ` +
              `file_path=${requestBody.prCommentFields.file_path}, ` +
              `explanation_length=${requestBody.prCommentFields.optimization_explanation?.length || 0}`,
          );

          let response: Response;
          try {
            response = await fetch(endpoint, {
              method: "POST",
              headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${session.accessToken}`,
                "X-CodeFlash-Source": "vscode-extension",
              },
              body: JSON.stringify(requestBody),
            });
          } catch (fetchError) {
            const fetchErrorMsg =
              fetchError instanceof Error
                ? fetchError.message
                : String(fetchError);
            this.logger.error(
              `[createPR] Network error: ${fetchErrorMsg}`,
              fetchError instanceof Error ? fetchError : undefined,
            );
            throw new Error(
              `Network error connecting to Codeflash API: ${fetchErrorMsg}. Please check your internet connection.`,
            );
          }

          this.logger.info(
            `[createPR] API response status: ${response.status} ${response.statusText}`,
          );

          if (!response.ok && response.status !== 202) {
            let errorMessage = "Failed to create pull request";
            try {
              const contentType = response.headers.get("content-type");
              if (contentType?.includes("json")) {
                const errorData = await response.json();
                this.logger.error(
                  `[createPR] API error response (JSON): ${JSON.stringify(errorData)}`,
                );
                errorMessage =
                  errorData.error || errorData.message || errorMessage;
              } else {
                const text = await response.text();
                this.logger.error(
                  `[createPR] API error response (text): ${text}`,
                );
                if (text) {
                  errorMessage = text;
                }
              }
            } catch {
              // Use default error message
            }

            // Provide more helpful error messages for common issues
            if (
              response.status === 401 ||
              errorMessage.includes("Unauthorized") ||
              errorMessage.includes("installation")
            ) {
              throw new Error(
                `The Codeflash GitHub App may not be installed on ${owner}/${repo}. ` +
                  `Please install it at https://github.com/apps/codeflash-ai and try again.`,
              );
            }

            if (response.status === 500) {
              throw new Error(
                `Server error creating PR. This may be because the Codeflash GitHub App ` +
                  `is not installed on ${owner}/${repo}. Install it at https://github.com/apps/codeflash-ai`,
              );
            }

            throw new Error(errorMessage);
          }

          // Handle response - can be JSON or text
          const contentType = response.headers.get("content-type");
          let prNumber: string;

          if (contentType?.includes("json")) {
            const data = await response.json();
            this.logger.info(
              `[createPR] API success response (JSON): ${JSON.stringify(data)}`,
            );

            // Handle pending approval response
            if (data.status === "pending_approval") {
              vscode.window.showInformationMessage(
                data.message || "This optimization requires approval",
              );
              return;
            }

            prNumber =
              typeof data === "number" ? String(data) : data.toString();
          } else {
            prNumber = await response.text();
            this.logger.info(
              `[createPR] API success response (text): ${prNumber}`,
            );
          }

          const prUrl = `https://github.com/${owner}/${repo}/pull/${prNumber}`;

          progress.report({ message: "PR created successfully!" });
          this.logger.info(`[createPR] PR created successfully: ${prUrl}`);

          // Update the task with the PR URL
          const tasks =
            this.globalState.get(GlobalStateKey.QueueTasks, []) || [];
          const updatedTasks = tasks.map((t) =>
            t.id === this.viewPayload.id ? { ...t, prUrl } : t,
          );
          this.globalState.set(GlobalStateKey.QueueTasks, updatedTasks);
          this.logger.info(
            `[createPR] Updated task ${this.viewPayload.id} with PR URL: ${prUrl}`,
          );

          const action = await vscode.window.showInformationMessage(
            `Pull request #${prNumber} created successfully!`,
            "Open PR",
            "Copy URL",
          );

          if (action === "Open PR") {
            vscode.env.openExternal(vscode.Uri.parse(prUrl));
          } else if (action === "Copy URL") {
            await vscode.env.clipboard.writeText(prUrl);
            vscode.window.showInformationMessage("PR URL copied to clipboard");
          }

          this.logger.info(`Successfully created PR #${prNumber}: ${prUrl}`);
        },
      );
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      this.logger.error(
        `Failed to create pull request: ${errorMessage}`,
        error instanceof Error ? error : undefined,
      );
      vscode.window.showErrorMessage(
        `Failed to create pull request: ${errorMessage}`,
      );
    }
  }

  private cleanupAndExit() {
    this.onPatchApplied();
    this._view?.dispose();
    this._view = undefined;
    this._explanationPanel?.dispose();
    this._explanationPanel = undefined;
    // Clean up content providers to prevent memory leaks
    this._contentProviderDisposables.forEach((d) => d.dispose());
    this._contentProviderDisposables = [];
  }

  private async showPatchWebview(
    patchContent: string,
    patchName: string,
  ): Promise<void> {
    const panel = vscode.window.createWebviewPanel(
      "patchViewer",
      patchName,
      vscode.ViewColumn.One,
      {
        enableScripts: true,
        retainContextWhenHidden: true,
        localResourceRoots: [
          vscode.Uri.joinPath(this.extensionUri, "media"),
          vscode.Uri.joinPath(this.extensionUri, SIDEBAR_BUNDLE_DIR, "assets"),
        ],
      },
    );
    const webview = panel.webview;

    panel.iconPath = vscode.Uri.joinPath(
      this.extensionUri,
      "media",
      "codeflash.svg",
    );

    const explanationMarkdown = await marked(this.explanation);

    const extraStyle = getStyleHeadTags(webview, this.extensionUri);

    const html = this.createWebviewHtml(
      patchContent,
      patchName,
      explanationMarkdown,
      extraStyle,
    );
    panel.webview.html = html;

    // Handle messages from webview
    panel.webview.onDidReceiveMessage((message) =>
      this.handleWebviewMessage(message),
    );

    panel.onDidDispose(() => {
      this._view = undefined;
    });

    this._view = panel;
  }
  private get workspaceRoot(): string {
    const root = this.globalState.get(GlobalStateKey.RootDir, "")!;
    return root;
  }

  private async applyWithConflicts(
    patch: StructuredPatch,
  ): Promise<Error | null> {
    this.logger.info("Applying patch with conflicts");

    if (this.merging) {
      return new Error("Pending merge is already running");
    }
    this.merging = true;

    const contextEntries = this.globalState.get(
      GlobalStateKey.ContextFiles,
      {},
    )![this.viewPayload.id];
    if (!contextEntries) {
      return new Error("no context entries for task" + this.viewPayload.id);
    }

    let base = "";

    const ancestorFile = this.removeFilesPrefixes(patch.oldFileName);
    for (const [worktreePath, content] of Object.entries(contextEntries)) {
      if (!worktreePath.includes(ancestorFile)) {
        continue;
      }
      base = content;
      break;
    }
    if (base === "") {
      return new Error("Couldn't find the target file in the context files");
    }

    const filePath = path.join(this.workspaceRoot, ancestorFile);

    const theirs = await fs.readFile(filePath, { encoding: "utf-8" });
    const ours = applyPatch(base, patch, { fuzzFactor: 99 });
    if (!ours) {
      return new Error("couldn't apply the optimization patch to the base");
    }

    this.logger.info("Starting 3-way merge...");

    // Run the merge operation asynchronously to avoid blocking the main thread
    const diffResult = await new Promise<Diff3Result[]>((resolve, reject) => {
      setTimeout(() => {
        try {
          const result = diff3Merge(
            ours.match(LINEBREAKS)!,
            base.match(LINEBREAKS)!,
            theirs.match(LINEBREAKS)!,
            {},
          );
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    });

    this.logger.info("3-way merge completed");

    // Here we note whether there are conflicts and format the results
    let mergedText = "";
    let hasConflict = false;
    for (const item of diffResult) {
      if (item.ok) {
        mergedText += item.ok.join("");
      }

      if (item.conflict) {
        hasConflict = true;
        mergedText += `<${"<".repeat(markerSize - 1)} ${ourName}\n`;
        mergedText += item.conflict.a.join("") + "\n";
        mergedText += `=${"=".repeat(markerSize - 1)}\n`;
        mergedText += item.conflict.b.join("") + "\n";
        mergedText += `>${">".repeat(markerSize - 1)} ${theirName}\n`;
      }
    }
    this.merging = false;

    if (hasConflict) {
      await fs.writeFile(filePath, mergedText, { encoding: "utf-8" });
    }
    return null;
  }

  /**
   * Removes git diff prefixes (a/ or b/) from file paths.
   */
  private removeFilesPrefixes = (filePath: string): string => {
    return filePath.replace(/^[ab]\//, "");
  };

  async applyPatch() {
    const patchPath = this.patchFile;

    const patchContent = await fs.readFile(patchPath, { encoding: "utf-8" });
    applyPatches(patchContent, {
      loadFile: (index, callback) => {
        const filePath = path.join(
          this.workspaceRoot,
          this.removeFilesPrefixes(index.newFileName),
        );
        fs.readFile(filePath, { encoding: "utf-8" })
          .then((content) => callback(null, content))
          .catch((err) => callback(err, ""));
      },
      complete: (err) => {
        if (err) {
          vscode.window.showErrorMessage(
            `Failed to apply patch: ${err.message}`,
          );
        }
      },
      patched: (index, content) => {
        if (content) {
          const ancestorFile = this.removeFilesPrefixes(index.oldFileName);
          const filePath = path.join(this.workspaceRoot, ancestorFile);
          fs.writeFile(filePath, content, { encoding: "utf-8" }).then(() =>
            this.cleanupAndExit(),
          );
          return;
        }

        // apply with conflicts
        this.applyWithConflicts(index).then((err) => {
          // Merge operation completed
          if (err) {
            captureException(err);
            vscode.window.showErrorMessage(
              `Failed to apply patch: ${err.message}`,
            );
          } else {
            this.cleanupAndExit();
          }
        });
        return;
      },
      fuzzFactor: 99,
    });
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private handleWebviewMessage(message: any): void {
    switch (message.command) {
      case "error":
        vscode.window.showErrorMessage(message.text);
        break;
      case "applyPatch":
        this.applyPatch();
        break;
      default:
        this.logger.warn(`Unknown webview message command: ${message.command}`);
    }
  }
  private createWebviewHtml(
    patchContent: string,
    fileName: string,
    explanationHtml: string,
    styles: string,
  ): string {
    const kind = vscode.window.activeColorTheme.kind;

    const isDark =
      kind === vscode.ColorThemeKind.Dark ||
      kind === vscode.ColorThemeKind.HighContrast;

    return `<!doctype html>
<html lang="en-us">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Patch: ${fileName}</title>

    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/${isDark ? "github-dark" : "github"}.min.css"
    />
    <link
      rel="stylesheet"
      type="text/css"
      href="https://cdn.jsdelivr.net/npm/diff2html/bundles/css/diff2html.min.css"
    />
    ${styles}

    <style>
        body {
            font-family: var(--vscode-font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);
            font-size: var(--vscode-font-size, 13px);
            background-color: ${isDark ? "var(--vscode-editor-background);" : "#f8f8f8;"}
            color: var(--vscode-editor-foreground, #d4d4d4);
            margin: 0;
            padding: 0;
            overflow-x: auto;
        }

        ${this.getLineProfilerStyles()}

        :root {
          --d2h-dark-bg-color: var(--vscode-editor-background);
          --d2h-dark-color: var(--vscode-editor-foreground);
        }

        /* Sticky header styling */
        .patch-header {
            position: sticky;
            top: 0;
            z-index: 1000;
            background-color: var(--vscode-sideBar-background, ${isDark ? "#252526" : "#f3f3f3"});
            border-bottom: 1px solid var(--vscode-panel-border, #454545);
            backdrop-filter: blur(8px);
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 16px 24px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        }

        .patch-info-left {
            display: flex;
            flex-direction: column;
            gap: 4px;
            flex: 1;
        }

        .patch-title {
            font-size: 16px;
            font-weight: 600;
            color: var(--vscode-editor-foreground, #d4d4d4);
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            gap: 8px;
            margin: 0;
        }

        .patch-icon {
            font-size: 18px;
        }

        .opt-badge {
            background: linear-gradient(135deg, rgba(15, 124, 55, 0.3),rgba(4, 107, 42, 0.3));
            color: var(--vscode-editor-foreground, #d4d4d4);
            padding: 2px 8px;
            border-radius: 12px;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.5px;
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .apply-button {
            background: var(--vscode-button-background, #0e639c);
            color: var(--vscode-button-foreground, #ffffff);
            border: none;
            padding: 4px 8px;
            border-radius: 8px;
            font-size: 14px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .apply-button:hover {
            background: var(--vscode-button-hoverBackground, #1177bb);
        }

        /* Content area with proper spacing */
        .content-area {
            padding: 20px;
        }

        /* VSCode theme integration for diff */
        .d2h-wrapper {
            font-family: var(--vscode-font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);
            font-size: var(--vscode-font-size, 13px);
        }

        .d2h-file-header {
            background-color: var(--vscode-editor-lineHighlightBackground, #2d2d30);
            background-color: var(--vscode-list-hoverBackground, #37373d);
            border-color: var(--vscode-panel-border, #454545);
            cursor: pointer;
        }

        .d2h-file-name {
            color: var(--vscode-editor-foreground, #d4d4d4);
        }

        .d2h-code-line {
            font-family: var(--vscode-editor-font-family, 'Cascadia Code', 'Courier New', monospace);
            font-size: var(--vscode-editor-font-size, 14px);
        }

        .d2h-code-side-linenumber {
          background-color: var(--vscode-editor-background, #1e1e1e) !important;
          border: 0 !important;
          padding: 0 !important;
          color: var(--vscode-editor-foreground, #d4d4d4) !important;
        }

        .d2h-file-side-diff {
            overflow-x: auto !important;
        }

        .d2h-file-list-wrapper {
          padding: 10px;
        }

        /* Explanation section styling */
        .explanation-section {
            margin-bottom: 24px;
            padding: 16px;
            background-color: var(--vscode-editor-background);
            border-radius: 8px;
        }

        .explanation-content {
            color: var(--vscode-editor-foreground, #d4d4d4);
            line-height: 1.6;
        }

        /* Scrollbar styling */
        ::-webkit-scrollbar {
            width: 14px;
            height: 14px;
        }

        ::-webkit-scrollbar-thumb {
            background-color: var(--vscode-scrollbarSlider-background, #424242);
            border-radius: 7px;
        }

        ::-webkit-scrollbar-thumb:hover {
            background-color: var(--vscode-scrollbarSlider-hoverBackground, #4f4f4f);
        }

        ::-webkit-scrollbar-track {
            background-color: var(--vscode-editor-background, #1e1e1e);
        }

        /* Responsive design */
        @media (max-width: 768px) {
            .patch-header {
                flex-direction: column;
                gap: 12px;
                align-items: stretch;
                text-align: center;
            }

            .patch-details {
                justify-content: center;
            }

            .apply-button {
                align-self: center;
            }
        }

        /* Line Profiler Styles */
        .lp-section {
          margin: 24px 0;
          border: 1px solid var(--vscode-panel-border, ${isDark ? "#454545" : "#e0e0e0"});
          border-radius: 8px;
          overflow: hidden;
          background: var(--vscode-editor-background);
        }

        .lp-section-header {
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 14px 18px;
          background: linear-gradient(135deg, var(--vscode-sideBar-background), var(--vscode-editor-background));
          cursor: pointer;
          user-select: none;
          border-bottom: 1px solid var(--vscode-panel-border, ${isDark ? "#454545" : "#e0e0e0"});
        }

        .lp-section-header:hover {
          background: var(--vscode-list-hoverBackground);
        }

        .lp-section-icon { font-size: 18px; }

        .lp-section-title {
          font-size: 14px;
          font-weight: 600;
          flex: 1;
        }

        .lp-toggle-icon {
          font-size: 12px;
          color: var(--vscode-descriptionForeground);
          transition: transform 0.2s ease;
        }

        .lp-section-content { padding: 16px; }

        .lp-comparison {
          display: grid;
          grid-template-columns: 1fr 1fr;
          gap: 20px;
        }

        @media (max-width: 1200px) {
          .lp-comparison { grid-template-columns: 1fr; }
        }

        .lp-panel {
          border: 1px solid var(--vscode-panel-border, ${isDark ? "#3c3c3c" : "#e0e0e0"});
          border-radius: 6px;
          overflow: hidden;
        }

        .lp-panel-header {
          padding: 10px 14px;
          background: var(--vscode-sideBar-background);
          border-bottom: 1px solid var(--vscode-panel-border, ${isDark ? "#3c3c3c" : "#e0e0e0"});
        }

        .lp-panel-title {
          font-weight: 600;
          font-size: 13px;
        }

        .lp-function-header {
          display: flex;
          justify-content: space-between;
          align-items: center;
          padding: 8px 12px;
          background: var(--vscode-textBlockQuote-background, ${isDark ? "rgba(127, 127, 127, 0.1)" : "rgba(0, 0, 0, 0.05)"});
          margin: 12px;
          border-radius: 4px;
        }

        .lp-function-name {
          font-weight: 600;
          font-size: 12px;
          color: var(--vscode-symbolIcon-functionForeground, ${isDark ? "#dcdcaa" : "#795e26"});
          font-family: var(--vscode-editor-font-family, monospace);
        }

        .lp-function-time {
          font-size: 11px;
          color: var(--vscode-descriptionForeground);
        }

        .lp-table {
          width: 100%;
          border-collapse: collapse;
          font-size: 12px;
          font-family: var(--vscode-editor-font-family, monospace);
        }

        .lp-th {
          text-align: left;
          padding: 8px 10px;
          font-weight: 600;
          font-size: 11px;
          text-transform: uppercase;
          letter-spacing: 0.5px;
          color: var(--vscode-descriptionForeground);
          background: var(--vscode-editor-lineHighlightBackground, ${isDark ? "rgba(255, 255, 255, 0.04)" : "rgba(0, 0, 0, 0.04)"});
          border-bottom: 1px solid var(--vscode-panel-border, ${isDark ? "#3c3c3c" : "#e0e0e0"});
        }

        .lp-th-code { width: 50%; }

        .lp-row { transition: background-color 0.15s ease; }

        .lp-row:hover {
          background-color: var(--vscode-list-hoverBackground) !important;
        }

        .lp-cell {
          padding: 6px 10px;
          border-bottom: 1px solid var(--vscode-panel-border, ${isDark ? "rgba(255, 255, 255, 0.05)" : "rgba(0, 0, 0, 0.05)"});
          vertical-align: middle;
        }

        .lp-hits, .lp-time, .lp-perhit {
          text-align: right;
          color: var(--vscode-descriptionForeground);
          white-space: nowrap;
        }

        .lp-percent {
          width: 100px;
          min-width: 100px;
        }

        .lp-percent-container {
          position: relative;
          height: 18px;
          background: ${isDark ? "rgba(127, 127, 127, 0.1)" : "rgba(0, 0, 0, 0.08)"};
          border-radius: 3px;
          overflow: hidden;
        }

        .lp-percent-bar {
          position: absolute;
          top: 0;
          left: 0;
          height: 100%;
          border-radius: 3px;
          transition: width 0.3s ease;
        }

        .lp-percent-text {
          position: absolute;
          right: 6px;
          top: 50%;
          transform: translateY(-50%);
          font-size: 10px;
          font-weight: 600;
        }

        .lp-code {
          font-family: var(--vscode-editor-font-family, monospace);
          font-size: 12px;
          white-space: pre;
          overflow-x: auto;
        }

        .lp-no-data {
          padding: 24px;
          text-align: center;
          color: var(--vscode-descriptionForeground);
          font-style: italic;
        }

        /* Heat map colors */
        .lp-row.hot-4 { background: rgba(220, 38, 38, 0.25); }
        .lp-row.hot-4 .lp-percent-bar { background: linear-gradient(90deg, rgba(220, 38, 38, 0.8), rgba(185, 28, 28, 0.9)); }

        .lp-row.hot-3 { background: rgba(234, 88, 12, 0.2); }
        .lp-row.hot-3 .lp-percent-bar { background: linear-gradient(90deg, rgba(234, 88, 12, 0.7), rgba(220, 38, 38, 0.7)); }

        .lp-row.hot-2 { background: rgba(245, 158, 11, 0.15); }
        .lp-row.hot-2 .lp-percent-bar { background: linear-gradient(90deg, rgba(245, 158, 11, 0.7), rgba(234, 88, 12, 0.6)); }

        .lp-row.hot-1 { background: rgba(250, 204, 21, 0.1); }
        .lp-row.hot-1 .lp-percent-bar { background: linear-gradient(90deg, rgba(250, 204, 21, 0.6), rgba(245, 158, 11, 0.5)); }

        .lp-row.cold { background: rgba(34, 197, 94, 0.08); }
        .lp-row.cold .lp-percent-bar { background: linear-gradient(90deg, rgba(34, 197, 94, 0.5), rgba(74, 222, 128, 0.4)); }

        /* Toggle buttons */
        .lp-view-toggle {
          display: flex;
          gap: 4px;
          margin-left: auto;
        }

        .lp-view-btn {
          background: var(--vscode-button-secondaryBackground);
          color: var(--vscode-button-secondaryForeground);
          border: none;
          padding: 4px 10px;
          border-radius: 4px;
          font-size: 11px;
          cursor: pointer;
          transition: all 0.15s;
        }

        .lp-view-btn:hover {
          background: var(--vscode-button-secondaryHoverBackground);
        }

        .lp-view-btn.active {
          background: var(--vscode-button-background);
          color: var(--vscode-button-foreground);
        }

        /* Split/Unified view containers */
        .lp-split-view { display: block; }
        .lp-split-view.hidden { display: none; }
        .lp-unified-view { display: none; }
        .lp-unified-view.active { display: block; }

        /* Diff row styles */
        .lp-row.row-del { background: rgba(248, 81, 73, 0.15) !important; }
        .lp-row.row-add { background: rgba(46, 160, 67, 0.15) !important; }
        .diff-marker { display: inline-block; width: 14px; font-weight: 600; margin-right: 4px; }
        .diff-del { color: #f85149; }
        .diff-add { color: #3fb950; }
    </style>

    <!-- Load Diff2HtmlUI script -->
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/diff2html/bundles/js/diff2html-ui.min.js"></script>
</head>
<body>
    <div class="patch-header">
        <div class="patch-info-left">
            <div class="patch-title">
                <span class="patch-icon">⚡</span>
                ${this.functionName}
                <span class="opt-badge">⏱️ ${this.formattedOriginalRuntime} <span class="codicon codicon-arrow-small-right"></span> ${this.formattedOptimizedRuntime}</span>
                <span class="opt-badge">🚀 ${this.speedup}</span>
                <span class="opt-badge">🎯 Optimization Quality: ${this.optimizationReview}</span>
            </div>
        </div>
        <button class="apply-button" id="apply-btn">
            <span class="codicon codicon-pass"></span>
            Apply Optimization
        </button>
    </div>

    <div class="content-area">
        <div id="myDiffElement"></div>

        <!-- Line Profiler Section -->
        ${
          this.originalLineProfiler || this.optimizedLineProfiler
            ? `
        <div class="lp-section">
          <div class="lp-section-header">
            <span class="lp-section-icon" onclick="toggleLineProfiler()">📊</span>
            <span class="lp-section-title" onclick="toggleLineProfiler()">Performance Profile Comparison</span>
            <div class="lp-view-toggle">
              <button class="lp-view-btn active" id="btn-split" onclick="event.stopPropagation(); setLpView('split')">Split</button>
              <button class="lp-view-btn" id="btn-unified" onclick="event.stopPropagation(); setLpView('unified')">Unified</button>
            </div>
            <span class="lp-toggle-icon" id="lp-toggle-icon" onclick="toggleLineProfiler()">▼</span>
          </div>
          <div class="lp-section-content" id="lp-content">
            <!-- Split View -->
            <div class="lp-split-view" id="lp-split-view">
              <div class="lp-comparison">
                <!-- Original Panel -->
                <div class="lp-panel">
                  <div class="lp-panel-header">
                    <span class="lp-panel-title">Original</span>
                  </div>
                  <div class="lp-function-header">
                    <span class="lp-function-name">${this.functionName}</span>
                    <span class="lp-function-time">Total Time: ${this.formattedOriginalRuntime}</span>
                  </div>
                  <table class="lp-table">
                    <thead>
                      <tr>
                        <th class="lp-th">Hits</th>
                        <th class="lp-th">Time</th>
                        <th class="lp-th">Per Hit</th>
                        <th class="lp-th">% Time</th>
                        <th class="lp-th lp-th-code">Line Contents</th>
                      </tr>
                    </thead>
                    <tbody id="lp-original-tbody"></tbody>
                  </table>
                </div>

                <!-- Optimized Panel -->
                <div class="lp-panel">
                  <div class="lp-panel-header">
                    <span class="lp-panel-title">Optimized</span>
                  </div>
                  <div class="lp-function-header">
                    <span class="lp-function-name">${this.functionName}</span>
                    <span class="lp-function-time">Total Time: ${this.formattedOptimizedRuntime}</span>
                  </div>
                  <table class="lp-table">
                    <thead>
                      <tr>
                        <th class="lp-th">Hits</th>
                        <th class="lp-th">Time</th>
                        <th class="lp-th">Per Hit</th>
                        <th class="lp-th">% Time</th>
                        <th class="lp-th lp-th-code">Line Contents</th>
                      </tr>
                    </thead>
                    <tbody id="lp-optimized-tbody"></tbody>
                  </table>
                </div>
              </div>
            </div>

            <!-- Unified View -->
            <div class="lp-unified-view" id="lp-unified-view">
              <div class="lp-panel">
                <div class="lp-panel-header">
                  <span class="lp-panel-title">Unified Diff</span>
                </div>
                <div class="lp-function-header">
                  <span class="lp-function-name">${this.functionName}</span>
                  <span class="lp-function-time">Original: ${this.formattedOriginalRuntime} → Optimized: ${this.formattedOptimizedRuntime}</span>
                </div>
                <table class="lp-table">
                  <thead>
                    <tr>
                      <th class="lp-th">Hits</th>
                      <th class="lp-th">Time</th>
                      <th class="lp-th">Per Hit</th>
                      <th class="lp-th">% Time</th>
                      <th class="lp-th lp-th-code">Line Contents</th>
                    </tr>
                  </thead>
                  <tbody id="lp-unified-tbody"></tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
        `
            : ""
        }

        ${
          explanationHtml
            ? `
        <div class="explanation-section">
            <div class="explanation-content">
                ${explanationHtml}
            </div>
        </div>
        `
            : ""
        }

    </div>

    <script>
        const vscode = acquireVsCodeApi();
        const diffString = ${JSON.stringify(patchContent)};

        // Line profiler data
        const lpOriginalData = ${JSON.stringify(this.originalLineProfiler || "")};
        const lpOptimizedData = ${JSON.stringify(this.optimizedLineProfiler || "")};

        function toggleLineProfiler() {
          const content = document.getElementById('lp-content');
          const icon = document.getElementById('lp-toggle-icon');
          if (content.style.display === 'none') {
            content.style.display = 'block';
            icon.textContent = '▼';
          } else {
            content.style.display = 'none';
            icon.textContent = '▶';
          }
        }

        function parseProfilerData(markdown) {
          if (!markdown) return { rows: [], unit: 1e-6 };
          const lines = markdown.split('\\n');
          const rows = [];
          let inTable = false;
          let unit = 1e-6; // Default to microseconds

          for (const line of lines) {
            // Extract timer unit from header: "# Timer unit: 1e-06 s"
            if (line.startsWith('# Timer unit:')) {
              const match = line.match(/Timer unit:\\s*([\\d.eE+-]+)/);
              if (match) {
                unit = parseFloat(match[1]);
              }
              continue;
            }
            if (line.startsWith('|') && line.includes('Hits')) {
              inTable = true;
              continue;
            }
            if (line.startsWith('|--') || line.startsWith('| --')) continue;
            if (inTable && line.startsWith('|')) {
              const cells = line.split('|').map(c => c.trim()).filter(c => c);
              if (cells.length >= 5) {
                rows.push({
                  hits: cells[0] || '',
                  time: cells[1] || '',
                  perHit: cells[2] || '',
                  percent: cells[3] || '',
                  code: cells.slice(4).join('|'),
                  percentNum: parseFloat(cells[3]) || 0
                });
              }
            }
          }
          return { rows, unit };
        }

        function getHeatClass(percent) {
          if (percent > 50) return 'hot-4';
          if (percent > 30) return 'hot-3';
          if (percent > 15) return 'hot-2';
          if (percent > 5) return 'hot-1';
          return 'cold';
        }

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

        function formatTime(timeStr, unit) {
          if (!timeStr || timeStr.trim() === '') return '';
          const str = timeStr.trim();

          // Try to extract number - handles "1.23e-05", "0.000123", "123.45"
          const rawNum = parseFloat(str);
          if (isNaN(rawNum)) return str;

          // Convert from timer units to seconds
          // unit is typically 1e-6 (microseconds), so num * unit = seconds
          const num = rawNum * (unit || 1e-6);

          // Format to human readable
          if (num >= 3600) {
            const hrs = Math.floor(num / 3600);
            const mins = Math.floor((num % 3600) / 60);
            const secs = (num % 60).toFixed(1);
            return hrs + 'h ' + mins + 'm ' + secs + 's';
          } else if (num >= 60) {
            const mins = Math.floor(num / 60);
            const secs = (num % 60).toFixed(1);
            return mins + 'm ' + secs + 's';
          } else if (num >= 1) {
            return num.toFixed(2) + 's';
          } else if (num >= 0.001) {
            return (num * 1000).toFixed(2) + 'ms';
          } else if (num >= 0.000001) {
            return (num * 1000000).toFixed(2) + 'μs';
          } else if (num > 0) {
            return (num * 1000000000).toFixed(2) + 'ns';
          }
          return '0s';
        }

        function renderProfilerTable(rows, tbodyId, unit) {
          const tbody = document.getElementById(tbodyId);
          if (!tbody) return;
          if (!rows.length) {
            tbody.innerHTML = '<tr><td colspan="5" class="lp-no-data">No profiler data available</td></tr>';
            return;
          }

          let html = '';
          rows.forEach(row => {
            const heatClass = getHeatClass(row.percentNum);
            const barWidth = Math.min(row.percentNum, 100);
            const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

            html += '<tr class="lp-row ' + heatClass + '">';
            html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
            html += '<td class="lp-cell lp-time">' + formatTime(row.time, unit) + '</td>';
            html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, unit) + '</td>';
            html += '<td class="lp-cell lp-percent">';
            html += '<div class="lp-percent-container">';
            html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
            html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
            html += '</div></td>';
            html += '<td class="lp-cell lp-code"><code>' + escapeHtml(row.code) + '</code></td>';
            html += '</tr>';
          });

          tbody.innerHTML = html;
        }

        function renderUnifiedTable(originalRows, optimizedRows, origUnit, optUnit) {
          const tbody = document.getElementById('lp-unified-tbody');
          if (!tbody) return;
          if (!originalRows.length && !optimizedRows.length) {
            tbody.innerHTML = '<tr><td colspan="5" class="lp-no-data">No profiler data available</td></tr>';
            return;
          }

          let html = '';

          // Original lines with heat colors
          originalRows.forEach(row => {
            const heatClass = getHeatClass(row.percentNum);
            const barWidth = Math.min(row.percentNum, 100);
            const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

            html += '<tr class="lp-row ' + heatClass + '">';
            html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
            html += '<td class="lp-cell lp-time">' + formatTime(row.time, origUnit) + '</td>';
            html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, origUnit) + '</td>';
            html += '<td class="lp-cell lp-percent">';
            html += '<div class="lp-percent-container">';
            html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
            html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
            html += '</div></td>';
            html += '<td class="lp-cell lp-code"><code><span class="diff-marker diff-del">-</span>' + escapeHtml(row.code) + '</code></td>';
            html += '</tr>';
          });

          // Optimized lines with heat colors
          optimizedRows.forEach(row => {
            const heatClass = getHeatClass(row.percentNum);
            const barWidth = Math.min(row.percentNum, 100);
            const percentDisplay = row.percentNum > 0 ? row.percentNum.toFixed(1) + '%' : '';

            html += '<tr class="lp-row ' + heatClass + '">';
            html += '<td class="lp-cell lp-hits">' + escapeHtml(row.hits) + '</td>';
            html += '<td class="lp-cell lp-time">' + formatTime(row.time, optUnit) + '</td>';
            html += '<td class="lp-cell lp-perhit">' + formatTime(row.perHit, optUnit) + '</td>';
            html += '<td class="lp-cell lp-percent">';
            html += '<div class="lp-percent-container">';
            html += '<div class="lp-percent-bar" style="width: ' + barWidth + '%"></div>';
            html += '<span class="lp-percent-text">' + percentDisplay + '</span>';
            html += '</div></td>';
            html += '<td class="lp-cell lp-code"><code><span class="diff-marker diff-add">+</span>' + escapeHtml(row.code) + '</code></td>';
            html += '</tr>';
          });

          tbody.innerHTML = html;
        }

        function setLpView(view) {
          const splitView = document.getElementById('lp-split-view');
          const unifiedView = document.getElementById('lp-unified-view');
          const btnSplit = document.getElementById('btn-split');
          const btnUnified = document.getElementById('btn-unified');

          if (view === 'split') {
            splitView.classList.remove('hidden');
            unifiedView.classList.remove('active');
            btnSplit.classList.add('active');
            btnUnified.classList.remove('active');
          } else {
            splitView.classList.add('hidden');
            unifiedView.classList.add('active');
            btnSplit.classList.remove('active');
            btnUnified.classList.add('active');
          }
        }

        document.addEventListener('DOMContentLoaded', function () {
            try {
                var targetElement = document.getElementById('myDiffElement');
                var configuration = {
                    drawFileList: true,
                    fileListToggle: false,
                    fileListStartVisible: false,
                    fileContentToggle: false,
                    matching: 'lines',
                    outputFormat: 'side-by-side',
                    synchronisedScroll: true,
                    highlight: true,
                    renderNothingWhenEmpty: false,
                    ${isDark ? 'colorScheme: "dark"' : 'colorScheme: "light"'}
                };

                var diff2htmlUi = new Diff2HtmlUI(targetElement, diffString, configuration);
                diff2htmlUi.draw();

                // Render line profiler tables
                const originalData = parseProfilerData(lpOriginalData);
                const optimizedData = parseProfilerData(lpOptimizedData);
                renderProfilerTable(originalData.rows, 'lp-original-tbody', originalData.unit);
                renderProfilerTable(optimizedData.rows, 'lp-optimized-tbody', optimizedData.unit);
                renderUnifiedTable(originalData.rows, optimizedData.rows, originalData.unit, optimizedData.unit);

            } catch (error) {
                console.error('Failed to render diff:', error);
                vscode.postMessage({
                    command: 'error',
                    text: 'Failed to render patch: ' + error.message
                });
            }

            const applyBtn = document.getElementById('apply-btn');
            if (applyBtn) {
                applyBtn.addEventListener('click', () => {
                    vscode.postMessage({
                        command: 'applyPatch',
                    });
                });
            }
        });

    </script>
</body>
</html>`;
  }

  private generateLineProfilerHtml(): string {
    const hasOriginal = this.originalLineProfilerReport.functions.length > 0;
    const hasOptimized = this.optimizedLineProfilerReport.functions.length > 0;

    if (!hasOriginal && !hasOptimized) {
      return "";
    }

    const generateTableHtml = (
      report: LineProfilerReport,
      label: string,
      totalTimeLabel: string,
    ): string => {
      if (report.functions.length === 0) {
        return `
          <div class="lp-panel">
            <div class="lp-panel-header">
              <span class="lp-panel-title">${label}</span>
            </div>
            <div class="lp-no-data">No profiler data available</div>
          </div>
        `;
      }

      let tablesHtml = "";
      for (const func of report.functions) {
        let rowsHtml = "";
        for (const entry of func.entries) {
          const heatClass = getHeatLevel(entry.percentTime);
          const barWidth = Math.min(entry.percentTime, 100);
          rowsHtml += `
            <tr class="lp-row ${heatClass}">
              <td class="lp-cell lp-hits">${entry.hits}</td>
              <td class="lp-cell lp-time">${entry.time}</td>
              <td class="lp-cell lp-perhit">${entry.perHit}</td>
              <td class="lp-cell lp-percent">
                <div class="lp-percent-container">
                  <div class="lp-percent-bar" style="width: ${barWidth}%"></div>
                  <span class="lp-percent-text">${entry.percentTime > 0 ? entry.percentTime.toFixed(1) + "%" : ""}</span>
                </div>
              </td>
              <td class="lp-cell lp-code"><code>${this.escapeHtml(entry.lineContents)}</code></td>
            </tr>
          `;
        }

        tablesHtml += `
          <div class="lp-function">
            <div class="lp-function-header">
              <span class="lp-function-name">${func.functionName}</span>
              <span class="lp-function-time">${totalTimeLabel}: ${func.totalTime}</span>
            </div>
            <table class="lp-table">
              <thead>
                <tr>
                  <th class="lp-th">Hits</th>
                  <th class="lp-th">Time</th>
                  <th class="lp-th">Per Hit</th>
                  <th class="lp-th">% Time</th>
                  <th class="lp-th lp-th-code">Line Contents</th>
                </tr>
              </thead>
              <tbody>
                ${rowsHtml}
              </tbody>
            </table>
          </div>
        `;
      }

      return `
        <div class="lp-panel">
          <div class="lp-panel-header">
            <span class="lp-panel-title">${label}</span>
          </div>
          ${tablesHtml}
        </div>
      `;
    };

    const originalHtml = generateTableHtml(
      this.originalLineProfilerReport,
      "Original",
      "Total Time",
    );
    const optimizedHtml = generateTableHtml(
      this.optimizedLineProfilerReport,
      "Optimized",
      "Total Time",
    );

    return `
      <div class="lp-section">
        <div class="lp-section-header" onclick="toggleLineProfiler()">
          <span class="lp-section-icon">📊</span>
          <span class="lp-section-title">Performance Profile Comparison</span>
          <span class="lp-toggle-icon" id="lp-toggle-icon">▼</span>
        </div>
        <div class="lp-section-content" id="lp-content">
          <div class="lp-comparison">
            ${originalHtml}
            ${optimizedHtml}
          </div>
        </div>
      </div>
      <script>
        function toggleLineProfiler() {
          const content = document.getElementById('lp-content');
          const icon = document.getElementById('lp-toggle-icon');
          if (content.style.display === 'none') {
            content.style.display = 'block';
            icon.textContent = '▼';
          } else {
            content.style.display = 'none';
            icon.textContent = '▶';
          }
        }
      </script>
    `;
  }

  private escapeHtml(text: string): string {
    return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
  }

  private getLineProfilerStyles(): string {
    return `
      /* Line Profiler Section Styles */
      .lp-section {
        margin: 24px 0;
        border: 1px solid var(--vscode-panel-border, #454545);
        border-radius: 8px;
        overflow: hidden;
        background: var(--vscode-editor-background);
      }

      .lp-section-header {
        display: flex;
        align-items: center;
        gap: 10px;
        padding: 14px 18px;
        background: linear-gradient(135deg, var(--vscode-sideBar-background), var(--vscode-editor-background));
        cursor: pointer;
        user-select: none;
        border-bottom: 1px solid var(--vscode-panel-border, #454545);
      }

      .lp-section-header:hover {
        background: var(--vscode-list-hoverBackground);
      }

      .lp-section-icon {
        font-size: 18px;
      }

      .lp-section-title {
        font-size: 14px;
        font-weight: 600;
        color: var(--vscode-editor-foreground);
        flex: 1;
      }

      .lp-toggle-icon {
        font-size: 12px;
        color: var(--vscode-descriptionForeground);
        transition: transform 0.2s ease;
      }

      .lp-section-content {
        padding: 16px;
      }

      .lp-comparison {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 20px;
      }

      @media (max-width: 1200px) {
        .lp-comparison {
          grid-template-columns: 1fr;
        }
      }

      .lp-panel {
        border: 1px solid var(--vscode-panel-border, #3c3c3c);
        border-radius: 6px;
        overflow: hidden;
      }

      .lp-panel-header {
        padding: 10px 14px;
        background: var(--vscode-sideBar-background);
        border-bottom: 1px solid var(--vscode-panel-border, #3c3c3c);
      }

      .lp-panel-title {
        font-weight: 600;
        font-size: 13px;
        color: var(--vscode-editor-foreground);
      }

      .lp-function {
        margin: 12px;
      }

      .lp-function-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 8px 12px;
        background: var(--vscode-textBlockQuote-background, rgba(127, 127, 127, 0.1));
        border-radius: 4px;
        margin-bottom: 8px;
      }

      .lp-function-name {
        font-weight: 600;
        font-size: 12px;
        color: var(--vscode-symbolIcon-functionForeground, #dcdcaa);
        font-family: var(--vscode-editor-font-family, monospace);
      }

      .lp-function-time {
        font-size: 11px;
        color: var(--vscode-descriptionForeground);
      }

      .lp-table {
        width: 100%;
        border-collapse: collapse;
        font-size: 12px;
        font-family: var(--vscode-editor-font-family, monospace);
      }

      .lp-th {
        text-align: left;
        padding: 8px 10px;
        font-weight: 600;
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        color: var(--vscode-descriptionForeground);
        background: var(--vscode-editor-lineHighlightBackground, rgba(255, 255, 255, 0.04));
        border-bottom: 1px solid var(--vscode-panel-border, #3c3c3c);
      }

      .lp-th-code {
        width: 50%;
      }

      .lp-row {
        transition: background-color 0.15s ease;
      }

      .lp-row:hover {
        background-color: var(--vscode-list-hoverBackground) !important;
      }

      .lp-cell {
        padding: 6px 10px;
        border-bottom: 1px solid var(--vscode-panel-border, rgba(255, 255, 255, 0.05));
        vertical-align: middle;
      }

      .lp-hits, .lp-time, .lp-perhit {
        text-align: right;
        color: var(--vscode-descriptionForeground);
        white-space: nowrap;
      }

      .lp-percent {
        width: 100px;
        min-width: 100px;
      }

      .lp-percent-container {
        position: relative;
        height: 18px;
        background: rgba(127, 127, 127, 0.1);
        border-radius: 3px;
        overflow: hidden;
      }

      .lp-percent-bar {
        position: absolute;
        top: 0;
        left: 0;
        height: 100%;
        border-radius: 3px;
        transition: width 0.3s ease;
      }

      .lp-percent-text {
        position: absolute;
        right: 6px;
        top: 50%;
        transform: translateY(-50%);
        font-size: 10px;
        font-weight: 600;
        color: var(--vscode-editor-foreground);
      }

      .lp-code {
        font-family: var(--vscode-editor-font-family, monospace);
        font-size: 12px;
        white-space: pre;
        overflow-x: auto;
      }

      .lp-code code {
        color: var(--vscode-editor-foreground);
      }

      .lp-no-data {
        padding: 24px;
        text-align: center;
        color: var(--vscode-descriptionForeground);
        font-style: italic;
      }

      /* Heat map colors */
      .lp-row.hot-4 {
        background: rgba(220, 38, 38, 0.25);
      }
      .lp-row.hot-4 .lp-percent-bar {
        background: linear-gradient(90deg, rgba(220, 38, 38, 0.8), rgba(185, 28, 28, 0.9));
      }

      .lp-row.hot-3 {
        background: rgba(234, 88, 12, 0.2);
      }
      .lp-row.hot-3 .lp-percent-bar {
        background: linear-gradient(90deg, rgba(234, 88, 12, 0.7), rgba(220, 38, 38, 0.7));
      }

      .lp-row.hot-2 {
        background: rgba(245, 158, 11, 0.15);
      }
      .lp-row.hot-2 .lp-percent-bar {
        background: linear-gradient(90deg, rgba(245, 158, 11, 0.7), rgba(234, 88, 12, 0.6));
      }

      .lp-row.hot-1 {
        background: rgba(250, 204, 21, 0.1);
      }
      .lp-row.hot-1 .lp-percent-bar {
        background: linear-gradient(90deg, rgba(250, 204, 21, 0.6), rgba(245, 158, 11, 0.5));
      }

      .lp-row.cold {
        background: rgba(34, 197, 94, 0.08);
      }
      .lp-row.cold .lp-percent-bar {
        background: linear-gradient(90deg, rgba(34, 197, 94, 0.5), rgba(74, 222, 128, 0.4));
      }
    `;
  }
}
