#!/usr/bin/env nix-shell
#!nix-shell -i bash -p curl gnused common-updater-scripts jq prefetch-npm-deps
# shellcheck shell=bash
set -euo pipefail

root="$(dirname "$(readlink -f "$0")")"
repo_root="$(git -C "$root" rev-parse --show-toplevel)"
cd "$repo_root"

github_api_curl_args=()
if [ -n "${GITHUB_TOKEN:-}" ]; then
    github_api_curl_args=(-u ":$GITHUB_TOKEN")
fi

playwright_browsers_file="$root/browsers.json"
playwright_driver_file="$root/driver.nix"
playwright_raw_repo_url="https://raw.githubusercontent.com/microsoft/playwright"
playwright_mcp_package_file="$root/../../../by-name/pl/playwright-mcp/package.nix"
browser_names=(chromium chromium-headless-shell firefox webkit ffmpeg)
browser_systems=(x86_64-linux aarch64-linux aarch64-darwin)

github_api_get() {
    curl "${github_api_curl_args[@]}" -fsSL "$1"
}

major_minor() {
    echo "${1%.*}"
}

python_version=$(github_api_get https://api.github.com/repos/microsoft/playwright-python/releases/latest | jq -r '.tag_name | sub("^v"; "")')
# Most of the time, this should be the latest stable release of the Node-based
# Playwright version, but upstream occasionally ships additional npm-only patch
# releases. Resolve the latest patch in the same major.minor series.
driver_version_url="https://github.com/microsoft/playwright-python/raw/v${python_version}/DRIVER_VERSION"
python_driver_version=$(curl -fsSL "$driver_version_url" | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+')
python_major_minor=$(major_minor "$python_driver_version")
resolve_driver_version_latest_patch() {
    local mm_escaped
    mm_escaped=${python_major_minor//./\\.}
    curl -fsSL "https://registry.npmjs.org/playwright" \
        | jq -r '.versions | keys[]' \
        | grep -E "^${mm_escaped}\.[0-9]+$" \
        | sort -V \
        | tail -n1
}
driver_version="$(resolve_driver_version_latest_patch)"
: "${driver_version:?failed to resolve driver version from npm for python major.minor ${python_major_minor}}"
: "${python_driver_version:?failed to resolve driver_version from ${driver_version_url}}"

# TODO: skip if update-source-version reported the same version
update-source-version playwright-driver "$driver_version"
update-source-version python3Packages.playwright "$python_version"

temp_dir=$(mktemp -d)
trap 'rm -rf "$temp_dir"' EXIT

# Update playwright-mcp package
driver_major_minor=$(major_minor "$driver_version")
resolve_mcp_version() {
    local releases_json
    local tag_name

    releases_json=$(github_api_get "https://api.github.com/repos/microsoft/playwright-mcp/releases?per_page=100")
    while IFS= read -r tag_name; do
        local mcp_version_candidate mcp_npm_url mcp_playwright_dep mcp_major_minor
        mcp_version_candidate=${tag_name#v}
        mcp_npm_url="https://registry.npmjs.org/@playwright/mcp/${mcp_version_candidate}"
        mcp_playwright_dep=$(
            curl -fsSL "$mcp_npm_url" \
                | jq -r '.dependencies.playwright // .dependencies["playwright-core"] // empty'
        ) || continue
        mcp_major_minor=$(echo "$mcp_playwright_dep" | grep -Eo '[0-9]+\.[0-9]+' | head -n1 || true)
        if [ "$mcp_major_minor" = "$driver_major_minor" ]; then
            echo "$mcp_version_candidate"
            return 0
        fi
    done < <(echo "$releases_json" | jq -r '.[].tag_name')
    return 1
}
mcp_version="$(resolve_mcp_version)" || {
    echo "Could not find a playwright-mcp release compatible with Playwright driver ${driver_version}" >&2
    exit 1
}
update-source-version playwright-mcp "$mcp_version"

# Update npmDepsHash for playwright-mcp
mcp_lock_file="${temp_dir}/playwright-mcp-package-lock.json"
curl -fsSL -o "$mcp_lock_file" "https://raw.githubusercontent.com/microsoft/playwright-mcp/v${mcp_version}/package-lock.json"
mcp_npm_hash=$(prefetch-npm-deps "$mcp_lock_file")

sed -E -i 's#\bnpmDepsHash = "[^"]*"#npmDepsHash = "'"$mcp_npm_hash"'"#' "$playwright_mcp_package_file"


# update binaries of browsers, used by playwright.
replace_sha() {
    local target_file="$1"
    local attr_name="$2"
    local new_hash="$3"

    sed -i "s|$attr_name = \".\{44,52\}\"|$attr_name = \"$new_hash\"|" "$target_file"
}

prefetch_browser() {
    local url="$1"
    local strip_root="$2"
    local fake="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
    local expr
    local out

    # Use real fetchzip via fake-hash trick. nix-prefetch wrapper is broken on
    # macOS (msteen/nix-prefetch#53), so parse the "got:" line from the
    # mismatch error of a fixed-output derivation instead.
    expr="(import \"$repo_root\" {}).fetchzip { url = \"$url\"; stripRoot = $strip_root; hash = \"$fake\"; }"
    if out=$(nix-build --no-out-link -E "$expr" 2>&1); then
        echo "prefetch_browser: unexpected build success for $url" >&2
        return 1
    fi
    echo "$out" | grep -Eo 'got:[[:space:]]+sha256-[A-Za-z0-9+/=]+' | awk '{print $NF}' | head -n1
}

browser_download_info() {
    local name="$1"
    local revision="$2"
    local browser_version="$3"

    nix-instantiate --eval --json --strict "$root/browser-downloads.nix" \
        --argstr name "$name" \
        --argstr revision "$revision" \
        --argstr browserVersion "$browser_version"
}

update_browser() {
    local name="$1"
    local revision
    local browser_version
    local download_info
    local system
    local url
    local strip_root

    revision="$(jq -r ".browsers[\"$name\"].revision" "$playwright_browsers_file")"
    browser_version="$(jq -r ".browsers[\"$name\"].browserVersion // empty" "$playwright_browsers_file")"
    download_info="$(browser_download_info "$name" "$revision" "$browser_version")"

    for system in "${browser_systems[@]}"; do
        url="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].url')"
        strip_root="$(echo "$download_info" | jq -r --arg system "$system" '.[$system].stripRoot')"
        replace_sha "$root/$name.nix" "$system" "$(prefetch_browser "$url" "$strip_root")"
    done
}

curl -fsSL \
    "https://raw.githubusercontent.com/microsoft/playwright/v${driver_version}/packages/playwright-core/browsers.json" \
    | jq '
      .comment = "This file is kept up to date via update.sh"
      | .browsers |= (
        [.[]
          | select(.installByDefault) | del(.installByDefault)]
          | map({(.name): . | del(.name)})
          | add
      )
    ' > "$playwright_browsers_file"

for browser in "${browser_names[@]}"; do
    update_browser "$browser"
done

# Update package-lock.json files for all npm deps that are built in playwright

# Download `package-lock.json` for a given sourceRoot path and update its hash.
update_hash() {
    local source_root_path="$1"
    local download_url
    local lock_file
    local new_hash
    local source_root_pattern

    download_url="${playwright_raw_repo_url}/v${driver_version}${source_root_path}/package-lock.json"
    lock_file="${temp_dir}/$(echo "$source_root_path" | tr '/.' '__').package-lock.json"
    curl -fsSL -o "$lock_file" "$download_url"
    new_hash=$(prefetch-npm-deps "$lock_file")

    source_root_pattern=$(printf '%s\n' "$source_root_path" | sed 's/[][\\/.*^$+?(){}|]/\\&/g')
    sed -E -i "/sourceRoot = \"\\\$\\{src.name\\}${source_root_pattern}\";/,/npmDepsHash = / s#npmDepsHash = \"[^\"]*\";#npmDepsHash = \"${new_hash}\";#" "$playwright_driver_file"
}

while IFS= read -r source_root_path; do
    update_hash "$source_root_path"
done < <(
    # shellcheck disable=SC2016
    sed -n 's#^[[:space:]]*sourceRoot = "${src.name}\(.*\)";.*$#\1#p' "$playwright_driver_file"
)
