import type { Device } from "@pkg/logic/domains/device/data";
import { formatErrorDetail } from "@pkg/logger";
import { execFile } from "node:child_process";
import { promisify } from "node:util";

export type InternalAction = "start" | "stop" | "restart";

const execFileAsync = promisify(execFile);

export class AndroidCommandExecutor {
    constructor(
        private readonly binary: string,
        private readonly timeoutMs: number,
    ) {}

    async prepareAssignedApp(device: Device, packageName: string) {
        const serial = `${device.host}:${device.wsPort}`;

        await this.run(["connect", serial]);
        await this.run(["-s", serial, "wait-for-device"]);
        await this.run(
            ["-s", serial, "shell", "am", "force-stop", packageName],
            { allowFailure: true },
        );
        const launchActivity = await this.resolveLauncherActivity(
            serial,
            packageName,
        );

        if (launchActivity) {
            await this.run([
                "-s",
                serial,
                "shell",
                "am",
                "start",
                "-n",
                launchActivity,
            ]);
        } else {
            await this.run([
                "-s",
                serial,
                "shell",
                "monkey",
                "-p",
                packageName,
                "-c",
                "android.intent.category.LAUNCHER",
                "1",
            ]);
        }

        return { serial };
    }

    async disconnect(device: Device) {
        const serial = `${device.host}:${device.wsPort}`;
        await this.run(["disconnect", serial], { allowFailure: true });
    }

    private async run(args: string[], options?: { allowFailure?: boolean }) {
        try {
            return await execFileAsync(this.binary, args, {
                timeout: this.timeoutMs,
            });
        } catch (error) {
            if (options?.allowFailure) {
                return { stdout: "", stderr: formatErrorDetail(error) };
            }
            throw error;
        }
    }

    private async resolveLauncherActivity(
        serial: string,
        packageName: string,
    ): Promise<string | null> {
        const result = await this.run(
            [
                "-s",
                serial,
                "shell",
                "cmd",
                "package",
                "resolve-activity",
                "--brief",
                "-a",
                "android.intent.action.MAIN",
                "-c",
                "android.intent.category.LAUNCHER",
                packageName,
            ],
            { allowFailure: true },
        );

        const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
        const lines = output
            .split("\n")
            .map((line) => line.trim())
            .filter(Boolean);

        for (const line of lines) {
            if (line.includes("/") && !line.startsWith("priority=")) {
                return line;
            }
        }

        return null;
    }
}
