"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildNodeChecks = buildNodeChecks;
exports.defaultPlatformForOS = defaultPlatformForOS;
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-explicit-any */
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
// ---------- helpers ----------
function exec(cmd, args, timeoutMs = 5000) {
    return (0, child_process_1.execFileSync)(cmd, args, {
        encoding: 'utf-8',
        timeout: timeoutMs,
        stdio: ['ignore', 'pipe', 'pipe'],
    }).trim();
}
function isExecutable(p) {
    try {
        const st = (0, fs_1.statSync)(p);
        if (!st.isFile())
            return false;
        // best-effort: check owner-execute bit on POSIX; on Windows mode bits aren't reliable
        if (process.platform === 'win32')
            return true;
        return (st.mode & 0o111) !== 0;
    }
    catch {
        return false;
    }
}
function compareSemver(a, b) {
    const pa = a
        .replace(/^v/, '')
        .split('.')
        .map(n => parseInt(n, 10));
    const pb = b
        .replace(/^v/, '')
        .split('.')
        .map(n => parseInt(n, 10));
    for (let i = 0; i < 3; i++) {
        const diff = (pa[i] || 0) - (pb[i] || 0);
        if (diff !== 0)
            return diff < 0 ? -1 : 1;
    }
    return 0;
}
function getAndroidSdkRoot() {
    return process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
}
function findAdbPath() {
    const root = getAndroidSdkRoot();
    if (!root)
        return undefined;
    const ext = process.platform === 'win32' ? '.exe' : '';
    const adbPath = path_1.default.join(root, 'platform-tools', `adb${ext}`);
    return (0, fs_1.existsSync)(adbPath) ? adbPath : undefined;
}
/**
 * Resolve a bundled asset path relative to the package root.
 * Mirrors the multi-candidate resolution used by scrcpyService so it works
 * in dev (src/doctor), tsc (dist/src/doctor) and webpack (dist/src) layouts.
 */
function resolveAsset(filename) {
    const candidates = [
        path_1.default.resolve(__dirname, '../../assets', filename), // src/doctor or dist/src
        path_1.default.resolve(__dirname, '../../../assets', filename), // dist/src/doctor
        path_1.default.resolve(__dirname, '../../../../assets', filename), // dist/src/services/<sub>
    ];
    return candidates.find(p => (0, fs_1.existsSync)(p));
}
// ---------- checks ----------
function nodeVersionCheck() {
    return {
        title: 'Node.js >= 18.0.0',
        severity: 'required',
        run: () => {
            const current = process.versions.node;
            if (compareSemver(current, '18.0.0') < 0) {
                return { ok: false, detail: `found v${current}, need >= 18.0.0` };
            }
            return { ok: true, detail: `v${current}` };
        },
    };
}
function ffmpegCheck() {
    return {
        title: 'ffmpeg (bundled via @ffmpeg-installer/ffmpeg)',
        severity: 'required',
        run: () => {
            let ffmpegPath;
            try {
                // eslint-disable-next-line @typescript-eslint/no-var-requires
                ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
            }
            catch (err) {
                return { ok: false, detail: `module load failed: ${err.message}` };
            }
            if (!ffmpegPath || !(0, fs_1.existsSync)(ffmpegPath)) {
                return { ok: false, detail: `binary missing at ${ffmpegPath || '<unknown>'}` };
            }
            try {
                const out = exec(ffmpegPath, ['-version']);
                const version = out
                    .split('\n')[0]
                    .replace(/^ffmpeg version /, '')
                    .split(' ')[0];
                return { ok: true, detail: version };
            }
            catch (err) {
                return { ok: false, detail: `executing ffmpeg failed: ${err.message}` };
            }
        },
    };
}
function scrcpyAssetCheck() {
    return {
        title: 'scrcpy-server asset',
        severity: 'required',
        run: () => {
            const p = resolveAsset('scrcpy-server');
            if (!p)
                return { ok: false, detail: 'assets/scrcpy-server not found' };
            return { ok: true, detail: path_1.default.relative(process.cwd(), p) };
        },
    };
}
function dfStreamerAssetCheck() {
    return {
        title: 'DFStreamer.ipa asset',
        severity: 'optional',
        run: () => {
            const p = resolveAsset('DFStreamer.ipa');
            if (!p) {
                return { ok: false, detail: 'assets/DFStreamer.ipa not found (iOS streaming disabled)' };
            }
            return { ok: true, detail: path_1.default.relative(process.cwd(), p) };
        },
    };
}
function androidSdkCheck(opts) {
    return {
        title: 'ANDROID_HOME / ANDROID_SDK_ROOT',
        severity: 'required',
        skip: () => (opts.platform === 'ios' ? 'platform=ios' : false),
        run: () => {
            const root = getAndroidSdkRoot();
            if (!root)
                return { ok: false, detail: 'env var not set' };
            if (!(0, fs_1.existsSync)(root))
                return { ok: false, detail: `path does not exist: ${root}` };
            return { ok: true, detail: root };
        },
    };
}
function adbCheck(opts) {
    return {
        title: 'adb (Android Platform Tools)',
        severity: 'required',
        skip: () => {
            if (opts.platform === 'ios')
                return 'platform=ios';
            if (!getAndroidSdkRoot())
                return 'ANDROID_HOME not set';
            return false;
        },
        run: () => {
            const adbPath = findAdbPath();
            if (!adbPath)
                return { ok: false, detail: 'platform-tools/adb missing in SDK root' };
            if (!isExecutable(adbPath))
                return { ok: false, detail: `not executable: ${adbPath}` };
            try {
                const out = exec(adbPath, ['version']);
                const m = out.match(/Android Debug Bridge version ([\d.]+)/);
                return { ok: true, detail: m ? m[1] : 'available' };
            }
            catch (err) {
                return { ok: false, detail: `adb version failed: ${err.message}` };
            }
        },
    };
}
function xcrunCheck(opts) {
    return {
        title: 'xcrun (Xcode command line tools)',
        severity: 'required',
        skip: () => {
            if (opts.platform === 'android')
                return 'platform=android';
            if (process.platform !== 'darwin')
                return `not macOS (${process.platform})`;
            return false;
        },
        run: () => {
            try {
                exec('xcrun', ['--version']);
                return { ok: true };
            }
            catch (err) {
                return { ok: false, detail: `xcrun not found: ${err.message}` };
            }
        },
    };
}
function simctlCheck(opts) {
    return {
        title: 'xcrun simctl (iOS simulators)',
        severity: 'required',
        skip: () => {
            if (opts.platform === 'android')
                return 'platform=android';
            if (process.platform !== 'darwin')
                return `not macOS (${process.platform})`;
            return false;
        },
        run: () => {
            try {
                exec('xcrun', ['simctl', 'help']);
                return { ok: true };
            }
            catch (err) {
                return { ok: false, detail: `simctl unavailable: ${err.message}` };
            }
        },
    };
}
function devicectlCheck(opts) {
    return {
        title: 'xcrun devicectl (iOS 17+ real devices)',
        severity: 'optional',
        skip: () => {
            if (opts.platform === 'android')
                return 'platform=android';
            if (process.platform !== 'darwin')
                return `not macOS (${process.platform})`;
            return false;
        },
        run: () => {
            try {
                exec('xcrun', ['devicectl', '--version']);
                return { ok: true };
            }
            catch (err) {
                return {
                    ok: false,
                    detail: `devicectl missing — install Xcode 15+ or set GO_IOS for older iOS`,
                };
            }
        },
    };
}
function goIosCheck(opts) {
    return {
        title: 'go-ios (iOS real-device tracker, optional)',
        severity: 'optional',
        skip: () => (opts.platform === 'android' ? 'platform=android' : false),
        run: () => {
            const goIos = process.env.GO_IOS;
            if (!goIos) {
                return { ok: false, detail: 'GO_IOS not set (devicectl fallback)' };
            }
            if (!(0, fs_1.existsSync)(goIos))
                return { ok: false, detail: `path does not exist: ${goIos}` };
            if (!isExecutable(goIos))
                return { ok: false, detail: `not executable: ${goIos}` };
            try {
                const out = exec(goIos, ['--version']);
                return { ok: true, detail: out.split('\n')[0] };
            }
            catch (err) {
                return { ok: false, detail: `go-ios --version failed: ${err.message}` };
            }
        },
    };
}
function hubReachabilityCheck(opts) {
    return {
        title: 'Hub reachability',
        severity: 'optional',
        skip: () => (!opts.hubUrl ? '--hub-url not provided' : false),
        run: async () => {
            const url = opts.hubUrl;
            try {
                // Lightweight TCP+HTTP probe via Node's built-in http(s); no axios import to keep doctor fast
                const u = new URL(url);
                // eslint-disable-next-line @typescript-eslint/no-var-requires
                const client = u.protocol === 'https:' ? require('https') : require('http');
                const result = await new Promise(resolve => {
                    const req = client.request({
                        method: 'GET',
                        host: u.hostname,
                        port: u.port || (u.protocol === 'https:' ? 443 : 80),
                        path: u.pathname || '/',
                        timeout: 4000,
                    }, (res) => {
                        resolve({ ok: true, detail: `HTTP ${res.statusCode}` });
                        res.resume();
                    });
                    req.on('error', (e) => resolve({ ok: false, detail: e.message }));
                    req.on('timeout', () => {
                        req.destroy();
                        resolve({ ok: false, detail: 'timeout after 4s' });
                    });
                    req.end();
                });
                return result;
            }
            catch (err) {
                return { ok: false, detail: `invalid URL: ${err.message}` };
            }
        },
    };
}
// ---------- assembly ----------
function buildNodeChecks(opts) {
    return [
        nodeVersionCheck(),
        ffmpegCheck(),
        scrcpyAssetCheck(),
        dfStreamerAssetCheck(),
        androidSdkCheck(opts),
        adbCheck(opts),
        xcrunCheck(opts),
        simctlCheck(opts),
        devicectlCheck(opts),
        goIosCheck(opts),
        hubReachabilityCheck(opts),
    ];
}
function defaultPlatformForOS() {
    // Only macOS supports iOS tooling
    return os_1.default.platform() === 'darwin' ? 'both' : 'android';
}
//# sourceMappingURL=checks.js.map