/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import * as vscode from 'vscode';
import * as childProcess from 'child_process';
import {
  getAdbPath,
  getBundleName,
  getLibPath,
  getWorkSpacePath,
  KeelsDeviceManager,
} from './utils';
import { DevtoolsProvider } from './DevtoolsProvider';
import { resolve } from 'path';

export async function perfActivate(
  context: vscode.ExtensionContext,
  deviceManager: KeelsDeviceManager,
  targetProvider: DevtoolsProvider,
) {
  const provider = new PerfViewProvider(context.extensionUri, deviceManager, targetProvider);
  context.subscriptions.push(
    vscode.window.registerWebviewViewProvider(PerfViewProvider.viewType, provider),
  );
  provider.updateOption();
}

export class PerfViewProvider implements vscode.WebviewViewProvider {
  public static readonly viewType = 'cpuperf';

  private _view?: vscode.WebviewView;
  private _keelsDeviceManager?: KeelsDeviceManager;
  private _targetPrivider?: DevtoolsProvider;
  constructor(
    private readonly _extensionUri: vscode.Uri,
    deviceManager: KeelsDeviceManager,
    targetPrivider: DevtoolsProvider,
  ) {
    this._keelsDeviceManager = deviceManager;
    this._targetPrivider = targetPrivider;
  }
  delay(ms: number) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  public resolveWebviewView(webviewView: vscode.WebviewView) {
    this._view = webviewView;

    webviewView.webview.options = {
      enableScripts: true,
      localResourceRoots: [this._extensionUri],
    };
    const exeBin = process.platform === 'linux' ? '/devtools' : '\\devtools.exe';
    const exePath = vscode.workspace.getConfiguration().get<string>('CJMP.sdkPath') + exeBin;
    type SplitResult = {
      prefix: string;
      suffix?: string;
    };

    const splitByDash = (str: string): SplitResult => {
      const index = str.indexOf('-');
      if (index === -1) {
        return { prefix: str };
      }
      return {
        prefix: str.substring(0, index),
        suffix: str.substring(index + 1),
      };
    };
    let projectRoot = getWorkSpacePath();
    const startTask = (message: any) => {
      const result = splitByDash(message.device);
      const platform = result.suffix?.toLowerCase();
      const output = projectRoot?.fsPath;
      const cmd =
        exePath +
        ` cpu start --device ${result.prefix} --platform ${platform} --pid ${message.pid} --output ${output}`;
      execNativeCommand(cmd);
    };

    const stopTask = async () => {
      execNativeCommand(exePath + ` cpu stop`);
      await this.delay(3000);
      const htmlPath = vscode.Uri.joinPath(projectRoot!, 'perf.html');
      this._targetPrivider?.showHtmlContent(htmlPath);
    };
    webviewView.webview.onDidReceiveMessage(async (message) => {
      switch (message.command) {
        case 'dropdownChanged':
          break;
        case 'refreshDropdown':
          this.updateOption();
          break;
        case 'toggleTask':
          if (message.state) {
            startTask(message);
          } else {
            stopTask();
          }
          break;
      }
    });
    webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
  }

  public async updateOption() {
    const devices = await this._keelsDeviceManager?.getKeelsDevices();
    const deivcesNames: string[] = [];
    const apps: string[] = [];
    const pids: string[] = [];
    if (!devices) {
      return;
    }
    for (let device of devices) {
      deivcesNames.push(device.id + '-' + device.platform);
    }
    const bundleName = await getBundleName();
    apps.push(bundleName);
    let pid = await getAndroidPid(bundleName);
    pids.push(pid);
    this.updateDropdownOptions(deivcesNames, apps, pids);
  }
  private _getHtmlForWebview(webview: vscode.Webview): string {
    return `
        <!DOCTYPE html>
        <html>
          <head>
            <style>
              .dropdown-container { padding: 10px; }
              select { width: 100%; padding: 5px; margin-bottom: 10px; }
              #refreshButton { 
                padding: 5px 12px;
                background-color: var(--vscode-button-background);
                color: var(--vscode-button-foreground);
                border: none;
                cursor: pointer;
              }
              #refreshButton:hover {
                background-color: var(--vscode-button-hoverBackground);
              }
              #statusButton { 
                padding: 5px 12px;
                background-color: var(--vscode-button-background);
                color: var(--vscode-button-foreground);
                border: none;
                cursor: pointer;
              }
              #statusButton:hover {
                background-color: var(--vscode-button-secondaryBackground);
              }
            </style>
          </head>
          <body>
            <div class="dropdown-container">
              <select id="myDropdown">
                <option value="option1">device</option>
              </select>
              <select id="configApp">
                <option value="fast">app</option>
              </select>
               <select id="configPid">
                <option value="fast">pid</option>
              </select>
              <button id="refreshButton">refresh</button>
               <button id="statusButton">start record</button>
            </div>
            <script>
              const vscode = acquireVsCodeApi();
              const dropdown = document.getElementById('myDropdown');
              const refreshButton = document.getElementById('refreshButton');
              const statusButton = document.getElementById('statusButton');
              const configApp = document.getElementById('configApp');
              const configPid = document.getElementById('configPid');
              let isRunning = false;
              dropdown.addEventListener('change', (event) => {
                vscode.postMessage({
                  command: 'dropdownChanged',
                  device: dropdown.value,
                  app: configApp.value,
                  pid: configPid.value
                });
              });
              configApp.addEventListener('change', (event) => {
                vscode.postMessage({
                  command: 'dropdownChanged',
                  device: dropdown.value,
                  app: configApp.value,
                  pid: configPid.value
                });
              });
              configPid.addEventListener('change',(event) => {
                vscode.postMessage({
                  command: 'dropdownChanged',
                  device: dropdown.value,
                  app: configApp.value,
                  pid: configPid.value
                });
              });
               refreshButton.addEventListener('click', () => {
                 vscode.postMessage({
                   command: 'refreshDropdown'  
                });
              });
              statusButton.addEventListener('click', () => {
                isRunning = !isRunning;
                statusButton.textContent = isRunning ? 'stop record' : 'start record';
                statusButton.classList.toggle('running', isRunning);
                vscode.postMessage({
                  command: 'toggleTask',
                  state: isRunning,
                  device: dropdown.value,
                  app: configApp.value,
                  pid: configPid.value
                });
              });
              window.addEventListener('message', (event) => {
                if (event.data.command === 'updateOptions') {
                   dropdown.innerHTML = event.data.options1
                   .map(opt => \`<option value="\${opt}">\${opt}</option>\`)
                   .join('');
                   configApp.innerHTML = event.data.options2
                   .map(opt => \`<option value="\${opt}">\${opt}</option>\`)
                   .join('');
                   configPid.innerHTML = event.data.options3
                   .map(opt => \`<option value="\${opt}">\${opt}</option>\`)
                   .join('');
                }
              });
            </script>
          </body>
        </html>
      `;
  }

  public updateDropdownOptions(options1: string[], options2: string[], options3: string[]) {
    if (this._view) {
      this._view.webview.postMessage({
        command: 'updateOptions',
        options1: options1,
        options2: options2,
        options3: options3,
      });
    }
  }
}

export async function getAndroidPid(bundleName: string): Promise<string> {
  let attemptCount = 0;
  let result;
  while (attemptCount < 10) {
    const adbExe = getAdbPath();
    if (!adbExe) {
      break;
    }
    const shellCmd = process.platform === 'win32' ?	    
      `${adbExe} shell ps -ef | findstr ${bundleName}` :
      `${adbExe} shell ps -ef | grep ${bundleName}`;
    let cmdResultInfo = await execNativeCommand(shellCmd);

    const lines = cmdResultInfo.split('\n');
    lines.forEach((line) => {
      const parts = line.trim().split(/\s+/);
      let minLength = 4;
      let bundleNameId = 7;
      if (parts.length >= minLength && parts[bundleNameId].toLowerCase() === bundleName) {
        result = parts[1];
      }
    });
    if (result) {
      break;
    }
    if (attemptCount < 10) {
      vscode.window.showInformationMessage(
        `Can not find a target process id for ${bundleName} for ${attemptCount} times, retrying...`,
      );
    }
    attemptCount++;
  }
  if (result) {
    vscode.window.showInformationMessage(`process Id ${result} is found.`);
    return result;
  } else {
    vscode.window.showInformationMessage(`failed to locate a process id`);
    return '';
  }
}

export function execNativeCommand(cmd: string): Promise<string> {
  return new Promise((resolve, reject) => {
    childProcess.exec(
      cmd,
      (error: any, stdout: string | PromiseLike<string>, stderr: string | any[]) => {
        if (error) {
          reject(error);
          return;
        }
        if (stderr && stderr.length > 0) {
          reject(stderr);
          return;
        }
        resolve(stdout);
      },
    );
  });
}