import { Logger } from "@perf-profiler/logger";
import { ChildProcess, execSync } from "child_process";
import { executeAsync, executeCommand } from "../shell";
import { getAbi } from "../getAbi";
import { detectCurrentAppBundleId } from "../detectCurrentAppBundleId";
import { CppProfilerName, UnixProfiler } from "./UnixProfiler";
import { ScreenRecorder } from "../ScreenRecorder";

export class AndroidProfiler extends UnixProfiler {
  private aTraceProcess: ChildProcess | null = null;

  installProfilerOnDevice(): void {
    super.installProfilerOnDevice();
    if (!this.aTraceProcess) this.startATrace();
  }

  stop(): void {
    this.stopATrace();
  }

  assertSupported(): void {
    const sdkVersion = parseInt(executeCommand("adb shell getprop ro.build.version.sdk"), 10);

    if (sdkVersion < 24) {
      throw new Error(
        `Your Android version (sdk API level ${sdkVersion}) is not supported. Supported versions > 23.`
      );
    }
  }

  protected pushExecutable(binaryTmpPath: string): void {
    executeCommand(`adb push ${binaryTmpPath} ${this.getDeviceProfilerPath()}`);
    executeCommand(`adb shell chmod 755 ${this.getDeviceProfilerPath()}`);
  }

  public getDeviceProfilerPath(): string {
    return `/data/local/tmp/${CppProfilerName}`;
  }

  protected stopATrace() {
    // We need to close this process, otherwise tests will hang
    Logger.debug("Stopping atrace process...");
    this.aTraceProcess?.kill();
    this.aTraceProcess = null;
  }

  protected startATrace() {
    Logger.debug("Stopping atrace and flushing output...");
    /**
     * Since output from the atrace --async_stop
     * command can be quite big, seems like buffer overflow can happen
     * Let's ignore the output then
     *
     * See https://stackoverflow.com/questions/63796633/spawnsync-bin-sh-enobufs
     */
    execSync("adb shell atrace --async_stop", { stdio: "ignore" });
    Logger.debug("Starting atrace...");
    this.aTraceProcess = executeAsync("adb shell atrace -c view -t 999");
  }

  public getDeviceCommand(command: string): string {
    return `adb shell ${command}`;
  }

  protected getAbi(): string {
    return getAbi();
  }

  public detectCurrentBundleId(): string {
    return detectCurrentAppBundleId().bundleId;
  }

  public supportFPS(): boolean {
    return true;
  }

  public getScreenRecorder(videoPath: string) {
    return new ScreenRecorder(videoPath);
  }

  async stopApp(bundleId: string) {
    execSync(`adb shell am force-stop ${bundleId}`);
    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
}
