"""Bun frontend build helpers for Wails."""

load("//wails:private/common.bzl", "bash_launcher", "write_manifest")

def _node_modules_root_from_path(path):
    marker = "/node_modules/"
    marker_index = path.find(marker)
    if marker_index < 0:
        return None
    return path[:marker_index + len("/node_modules")]

def _dirname(path):
    index = path.rfind("/")
    if index < 0:
        return "."
    return path[:index] or "."

def _wails_bun_frontend_dist_impl(ctx):
    out_dir = ctx.actions.declare_directory(ctx.label.name)

    manifest_srcs = list(ctx.files.srcs)
    if ctx.file.package_json.path not in [src.path for src in manifest_srcs]:
        manifest_srcs.append(ctx.file.package_json)

    manifest = write_manifest(
        ctx,
        ctx.label.name + ".manifest",
        manifest_srcs,
        ctx.attr.strip_prefix,
    )

    if not ctx.files.node_modules:
        fail("wails_bun_frontend_dist requires a non-empty node_modules tree")

    workspace_marker = ""
    if ctx.attr.workspace_dir:
        workspace_marker = "/%s/node_modules/" % ctx.attr.workspace_dir.strip("/")

    shortest_path = None
    shared_node_modules_root = None
    workspace_node_modules_root = None

    for src in ctx.files.node_modules:
        if shortest_path == None or len(src.path) < len(shortest_path):
            shortest_path = src.path
        if workspace_marker and workspace_marker in src.path and workspace_node_modules_root == None:
            workspace_node_modules_root = _node_modules_root_from_path(src.path)

    if shortest_path:
        shared_node_modules_root = _node_modules_root_from_path(shortest_path)

    node_modules_root = workspace_node_modules_root or shared_node_modules_root
    if node_modules_root == None or shared_node_modules_root == None:
        fail("unable to determine node_modules roots from node_modules inputs")

    package_json_rel = ctx.file.package_json.short_path
    if ctx.attr.strip_prefix and package_json_rel.startswith(ctx.attr.strip_prefix):
        package_json_rel = package_json_rel[len(ctx.attr.strip_prefix):]
    package_dir = _dirname(package_json_rel)

    args = ctx.actions.args()
    args.add("--build-script", ctx.attr.build_script)
    args.add("--manifest", manifest.path)
    args.add("--node-modules-root", node_modules_root)
    args.add("--out", out_dir.path)
    args.add("--package-dir", package_dir)
    args.add("--shared-node-modules-root", shared_node_modules_root)
    args.add("--bun-darwin-aarch64", ctx.file._bun_darwin_aarch64.path)
    args.add("--bun-darwin-x64", ctx.file._bun_darwin_x64.path)
    args.add("--bun-linux-aarch64", ctx.file._bun_linux_aarch64.path)
    args.add("--bun-linux-x64", ctx.file._bun_linux_x64.path)
    args.add("--bun-windows-x64", ctx.file._bun_windows_x64.path)
    if ctx.attr.workspace_dir:
        args.add("--workspace-dir", ctx.attr.workspace_dir)

    ctx.actions.run(
        executable = ctx.executable._tool,
        arguments = [args],
        inputs = depset(
            manifest_srcs +
            ctx.files.node_modules + [
                manifest,
                ctx.file._bun_darwin_aarch64,
                ctx.file._bun_darwin_x64,
                ctx.file._bun_linux_aarch64,
                ctx.file._bun_linux_x64,
                ctx.file._bun_windows_x64,
            ],
        ),
        outputs = [out_dir],
        tools = [ctx.attr._tool[DefaultInfo].files_to_run],
        mnemonic = "WailsBunFrontendDist",
        progress_message = "Building Bun frontend dist for %s" % ctx.label,
    )

    return [DefaultInfo(files = depset([out_dir]))]

def _wails_bun_frontend_dev_impl(ctx):
    launcher = ctx.actions.declare_file(ctx.label.name)
    package_dir = _dirname(ctx.file.package_json.short_path)

    resolve_lines = """
tool="$(resolve_runfile "{tool_short_path}")"
bun_darwin_aarch64="$(resolve_runfile "{bun_darwin_aarch64_short_path}")"
bun_darwin_x64="$(resolve_runfile "{bun_darwin_x64_short_path}")"
bun_linux_aarch64="$(resolve_runfile "{bun_linux_aarch64_short_path}")"
bun_linux_x64="$(resolve_runfile "{bun_linux_x64_short_path}")"
bun_windows_x64="$(resolve_runfile "{bun_windows_x64_short_path}")"
""".format(
        tool_short_path = ctx.executable._tool.short_path,
        bun_darwin_aarch64_short_path = ctx.file._bun_darwin_aarch64.short_path,
        bun_darwin_x64_short_path = ctx.file._bun_darwin_x64.short_path,
        bun_linux_aarch64_short_path = ctx.file._bun_linux_aarch64.short_path,
        bun_linux_x64_short_path = ctx.file._bun_linux_x64.short_path,
        bun_windows_x64_short_path = ctx.file._bun_windows_x64.short_path,
    )

    command_lines = """
exec "$tool" \
  --bun-darwin-aarch64 "$bun_darwin_aarch64" \
  --bun-darwin-x64 "$bun_darwin_x64" \
  --bun-linux-aarch64 "$bun_linux_aarch64" \
  --bun-linux-x64 "$bun_linux_x64" \
  --bun-windows-x64 "$bun_windows_x64" \
  --package-dir {package_dir} \
  --script {script}
""".format(
        package_dir = _shell_quote(ctx.attr.workspace_dir or package_dir),
        script = _shell_quote(ctx.attr.script),
    )

    ctx.actions.write(
        output = launcher,
        is_executable = True,
        content = bash_launcher(resolve_lines, command_lines),
    )

    runfiles = ctx.runfiles(
        files = [
            ctx.executable._tool,
            ctx.file._bun_darwin_aarch64,
            ctx.file._bun_darwin_x64,
            ctx.file._bun_linux_aarch64,
            ctx.file._bun_linux_x64,
            ctx.file._bun_windows_x64,
        ] + ([ctx.file.package_json] if ctx.file.package_json else []),
        transitive_files = depset(transitive = [
            ctx.attr._tool[DefaultInfo].default_runfiles.files,
        ]),
    )

    return [DefaultInfo(executable = launcher, runfiles = runfiles)]

def _shell_quote(value):
    return "'" + value.replace("'", "'\"'\"'") + "'"

wails_bun_frontend_dist = rule(
    implementation = _wails_bun_frontend_dist_impl,
    attrs = {
        "build_script": attr.string(default = "build"),
        "node_modules": attr.label(
            mandatory = True,
            allow_files = True,
        ),
        "package_json": attr.label(
            mandatory = True,
            allow_single_file = True,
        ),
        "srcs": attr.label_list(
            mandatory = True,
            allow_files = True,
        ),
        "strip_prefix": attr.string(default = ""),
        "workspace_dir": attr.string(default = ""),
        "_tool": attr.label(
            default = "//wails_bun/tools:frontend_dist_action",
            cfg = "exec",
            executable = True,
        ),
        "_bun_darwin_aarch64": attr.label(
            default = "@bun_darwin_aarch64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_darwin_x64": attr.label(
            default = "@bun_darwin_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_linux_aarch64": attr.label(
            default = "@bun_linux_aarch64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_linux_x64": attr.label(
            default = "@bun_linux_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_windows_x64": attr.label(
            default = "@bun_windows_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
    },
)

_wails_bun_frontend_dev = rule(
    implementation = _wails_bun_frontend_dev_impl,
    attrs = {
        "data": attr.label_list(allow_files = True),
        "node_modules": attr.label(
            mandatory = True,
            allow_files = True,
        ),
        "package_json": attr.label(
            mandatory = True,
            allow_single_file = True,
        ),
        "script": attr.string(default = "dev"),
        "workspace_dir": attr.string(default = ""),
        "_tool": attr.label(
            default = "//wails_bun/tools:frontend_dev_server",
            cfg = "exec",
            executable = True,
        ),
        "_bun_darwin_aarch64": attr.label(
            default = "@bun_darwin_aarch64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_darwin_x64": attr.label(
            default = "@bun_darwin_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_linux_aarch64": attr.label(
            default = "@bun_linux_aarch64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_linux_x64": attr.label(
            default = "@bun_linux_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
        "_bun_windows_x64": attr.label(
            default = "@bun_windows_x64//:bun",
            cfg = "exec",
            allow_single_file = True,
        ),
    },
    executable = True,
)

def wails_bun_frontend_dev(
        name,
        data,
        node_modules,
        package_json,
        script = "dev",
        workspace_dir = "",
        tags = None,
        visibility = None):
    _wails_bun_frontend_dev(
        name = name,
        data = data,
        node_modules = node_modules,
        package_json = package_json,
        script = script,
        tags = tags,
        visibility = visibility,
        workspace_dir = workspace_dir,
    )
