#!/usr/bin/env python3
# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import base64
import hashlib
import os
import re
import shutil
import subprocess
import sys
import tempfile
import time
import urllib.request
import installer

KEY_URL = "https://dl.google.com/linux/linux_signing_key.pub"
KEY_FINGERPRINT = "EB4C1BFD4F042F6DDDCCEC917721F63BD38B4796"

KEY_INCLUDE_TEMPLATE = """\
# This file is automatically generated by update_key_include.py
# Do not edit this file directly.

{key_info}
PGP_KEY_DATA=$(cat <<KEYDATA
{key_data}
KEYDATA
)

PGP_KEY_CHECKSUM="{checksum}"

PGP_SUBKEYS="{subkeys}"
"""

REPO_PACKAGE_INCLUDE_TEMPLATE = """\
# This file is automatically generated by update_key_include.py.
# Do not edit this file directly.
REPO_PACKAGE_VERSION={version}
REPO_PACKAGE_TIMESTAMP={timestamp}
REPO_PACKAGE_HASH={hash}
"""


def crc24(data):
    crc = 0x00B704CE
    for byte in data:
        crc ^= byte << 16
        for _ in range(8):
            crc <<= 1
            if crc & 0x01000000:
                crc ^= 0x01864CFB
    return crc & 0x00FFFFFF


def get_checksum(raw_data):
    checksum = crc24(raw_data)
    checksum_bytes = checksum.to_bytes(3, byteorder="big")
    return "=" + base64.b64encode(checksum_bytes).decode("utf-8")


def main():
    script_dir = os.path.dirname(os.path.realpath(__file__))
    key_include_path = os.path.join(script_dir, "key.include")

    # Read current key.include
    with open(key_include_path, "r") as f:
        content = f.read()

    # Download latest key
    print(f"Downloading key from {KEY_URL}...")
    with urllib.request.urlopen(KEY_URL) as response:
        latest_key_armored = response.read().decode("utf-8")

    # Use gpg to filter and format the key
    with tempfile.TemporaryDirectory() as tmpdir:
        gnupg_home = os.path.join(tmpdir, "gnupg")
        os.makedirs(gnupg_home)
        env = os.environ.copy()
        env["GNUPGHOME"] = gnupg_home

        # Import the key
        input_key_path = os.path.join(tmpdir, "input_key.pub")
        with open(input_key_path, "w") as f:
            f.write(latest_key_armored)

        subprocess.run(
            ["gpg", "--import", input_key_path],
            env=env,
            check=True,
            capture_output=True,
        )

        # Export minimal key (only active subkeys)
        export_cmd = [
            "gpg",
            "--export",
            "--armor",
            "--export-options",
            "export-minimal",
            KEY_FINGERPRINT,
        ]
        result = subprocess.run(
            export_cmd, env=env, check=True, capture_output=True
        )
        new_key_armored = result.stdout.decode("utf-8")

        # Get key info for comments
        list_cmd = ["gpg", "--list-keys", KEY_FINGERPRINT]
        result = subprocess.run(
            list_cmd, env=env, check=True, capture_output=True
        )
        key_info = result.stdout.decode("utf-8")

        with tempfile.TemporaryDirectory() as tmpdir2:
            gnupg_home2 = os.path.join(tmpdir2, "gnupg")
            os.makedirs(gnupg_home2)
            env2 = os.environ.copy()
            env2["GNUPGHOME"] = gnupg_home2

            subprocess.run(
                ["gpg", "--import"],
                env=env2,
                input=new_key_armored.encode("utf-8"),
                check=True,
                capture_output=True,
            )

            list_cmd2 = ["gpg", "--with-colons", "--list-keys", KEY_FINGERPRINT]
            result = subprocess.run(
                list_cmd2, env=env2, check=True, capture_output=True
            )
            key_info_colons = result.stdout.decode("utf-8")

    subkeys = []
    for line in key_info_colons.splitlines():
        parts = line.split(":")
        if parts[0] == "sub":
            subkeys.append(parts[4])

    # Extract data and checksum from armored key
    lines = new_key_armored.splitlines()
    data_lines = []
    checksum = ""
    for line in lines:
        if line.startswith("-----"):
            continue
        if line.startswith("="):
            checksum = line
            continue
        if line:
            data_lines.append(line)

    new_data = "\n".join(data_lines)

    # Verify checksum
    raw_data = base64.b64decode("".join(data_lines))
    calculated_checksum = get_checksum(raw_data)
    if calculated_checksum != checksum:
        raise ValueError(
            f"Calculated checksum {calculated_checksum} "
            f"does not match GPG checksum {checksum}"
        )

    # Format key info as comments
    new_comments = "\n".join(
        f"# {line}" for line in key_info.splitlines() if line.strip()
    )

    # Update content
    output = KEY_INCLUDE_TEMPLATE.format(
        checksum=checksum,
        key_info=new_comments,
        key_data=new_data,
        subkeys=" ".join(subkeys),
    )

    with open(key_include_path, "w") as f:
        f.write(output)

    print("Updated key.include")

    update_repo_package_include(script_dir)


def update_repo_package_include(script_dir):
    repo_include_path = os.path.join(
        script_dir, "..", "debian", "repo_package.include"
    )
    if not os.path.exists(repo_include_path):
        raise FileNotFoundError(
            f"repo_package.include not found at {repo_include_path}"
        )
    with open(repo_include_path, "r") as f:
        repo_content = f.read()
    repo_version_match = re.search(r"REPO_PACKAGE_VERSION=(\d+)", repo_content)
    if not repo_version_match:
        raise RuntimeError(
            f"REPO_PACKAGE_VERSION not found in {repo_include_path}"
        )
    new_repo_version = int(repo_version_match.group(1)) + 1
    new_timestamp = int(time.time())
    with open(repo_include_path, "w") as f:
        f.write(
            REPO_PACKAGE_INCLUDE_TEMPLATE.format(
                version=new_repo_version,
                timestamp=new_timestamp,
                hash="DUMMY",
            )
        )
    with tempfile.TemporaryDirectory() as tmpdir:
        new_hash = installer.compute_repo_package_hash_for_presubmit(
            os.path.join(script_dir, ".."), tmpdir
        )
    with open(repo_include_path, "w") as f:
        f.write(
            REPO_PACKAGE_INCLUDE_TEMPLATE.format(
                version=new_repo_version,
                timestamp=new_timestamp,
                hash=new_hash,
            )
        )
    print(f"Updated repo_package.include to version {new_repo_version}")


if __name__ == "__main__":
    if "--repo-only" in sys.argv:
        script_dir = os.path.dirname(os.path.abspath(__file__))
        update_repo_package_include(script_dir)
    else:
        main()
