usingnamespace @import("./global.zig");

const which = @import("./which.zig").which;
const std = @import("std");

pub const NPMClient = struct {
    bin: string,
    tag: Tag,

    pub const Tag = enum {
        npm,
        yarn,
        pnpm,
    };

    // This check adds around 150ms
    // so...if we do do this, we should do it in a separate thread
    pub fn isYarnBerry(allocator: *std.mem.Allocator, cwd_dir: string, yarn_path: string) bool {
        var args = [_]string{ yarn_path, "--version" };
        var term = std.ChildProcess.exec(.{
            .argv = &args,
            .allocator = allocator,
            .cwd = if (cwd_dir.len > 1) std.mem.trimRight(u8, cwd_dir, "/") else cwd_dir,
        }) catch return true;
        defer allocator.free(term.stderr);
        defer allocator.free(term.stdout);

        if (term.stdout.len == 0) return true;
        return term.stdout[0] != '1';
    }

    pub fn detect(allocator: *std.mem.Allocator, realpath_buf: *[std.fs.MAX_PATH_BYTES]u8, PATH: string, cwd: string, comptime allow_yarn: bool) !?NPMClient {

        // We say:
        // - pnpm if it exists, is the default. its most esoteric, so if you have it installed, you prob want it.
        // - yarn if it exists and it is yarn 1, its the default (yarn 2 or later is not supported)
        // - else npm

        const out_path = brk: {
            var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;

            const path: [:0]const u8 = if (comptime allow_yarn)
                which(
                    &path_buf,
                    PATH,
                    cwd,
                    "pnpm",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "yarn",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "npm",
                ) orelse ""
            else
                which(
                    &path_buf,
                    PATH,
                    cwd,
                    "pnpm",
                ) orelse which(
                    &path_buf,
                    PATH,
                    cwd,
                    "npm",
                ) orelse "";

            var file = std.fs.openFileAbsoluteZ(path, .{ .read = true }) catch return null;
            defer file.close();
            break :brk std.os.getFdPath(file.handle, realpath_buf) catch return null;
        };

        const basename = std.fs.path.basename(std.mem.span(out_path));
        if (basename.len == 0) return null;

        // if (comptime allow_yarn) {
        //     if (std.mem.indexOf(u8, basename, "yarn") != null) {
        //         if (isYarnBerry(allocator, cwd, out_path)) {
        //             return try detect(allocator, realpath_buf, PATH, cwd, false);
        //         }
        //     }
        // }

        if (strings.contains(basename, "pnpm")) {
            return NPMClient{ .bin = out_path, .tag = .pnpm };
        }

        if (strings.contains(basename, "yarn")) {
            return NPMClient{ .bin = out_path, .tag = .yarn };
        }

        if (strings.contains(basename, "npm")) {
            return NPMClient{ .bin = out_path, .tag = .npm };
        }

        return null;
    }
};
