#!/usr/bin/env python3
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import hashlib
import os
import shutil
import sys
import tempfile
from pathlib import Path
from typing import Any

import requests
import yaml


def change_to_script_directory() -> None:
    # Get the absolute path of the script
    script_path = Path(__file__).resolve()
    # Get the directory name of the script
    script_dir = script_path.parent
    # Change the current working directory to the script's directory
    os.chdir(script_dir)
    print(f"\nChanged working directory to: {script_dir}\n")


def load_yaml(file_path: Path) -> dict[str, Any]:
    """Load and parse the YAML file."""
    with file_path.open("r") as f:
        data = yaml.safe_load(f)
    return data


def create_temp_directory() -> Path:
    """Create the translations-artifacts directory in the system's temp directory."""
    temp_dir = Path(tempfile.gettempdir()) / "translations-artifacts"
    temp_dir.mkdir(parents=True, exist_ok=True)
    return temp_dir


def download_file(url: str, dest_path: Path) -> None:
    """Download a file from a URL to the destination path."""
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with dest_path.open("wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)


def verify_sha256(file_path: Path, expected_sha: str) -> None:
    """Verify the SHA256 hash of the downloaded file."""
    sha256 = hashlib.sha256()
    with file_path.open("rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            sha256.update(chunk)
    computed_sha = sha256.hexdigest()
    if computed_sha.lower() != expected_sha.lower():
        raise ValueError(
            f"SHA256 mismatch for {file_path.name}: expected {expected_sha}, got {computed_sha}"
        )


def verify_size(file_path: Path, expected_size: int) -> None:
    """Verify the size of the downloaded file."""
    actual_size = file_path.stat().st_size
    if actual_size != expected_size:
        raise ValueError(
            f"Size mismatch for {file_path.name}: expected {expected_size}, got {actual_size}"
        )


def clean_up(temp_dir: Path) -> None:
    """Remove the temporary directory and its contents."""
    if temp_dir.exists() and temp_dir.is_dir():
        shutil.rmtree(temp_dir)
        print(f"Cleaned up temporary directory: {temp_dir}")


def main() -> None:
    # Change the working directory to the script's location
    change_to_script_directory()

    # Define the path to the YAML file
    yaml_relative_path = Path(
        "./../../../../../taskcluster/kinds/fetch/translations-fetch.yml"
    ).resolve()

    if not yaml_relative_path.is_file():
        print(f"YAML file not found at {yaml_relative_path}")
        sys.exit(1)

    # Load YAML data
    data = load_yaml(yaml_relative_path)

    # Create temporary directory
    temp_dir = create_temp_directory()

    # Keep track of downloaded files for cleanup in case of failure
    downloaded_files = []

    try:
        for artifact_key, artifact_info in data.items():
            description = artifact_info.get("description", "No description")
            fetch_info = artifact_info.get("fetch", {})
            artifact_name = fetch_info.get("artifact-name")
            url = fetch_info.get("url")
            expected_sha = fetch_info.get("sha256")
            expected_size = fetch_info.get("size")

            if not all([artifact_name, url, expected_sha, expected_size]):
                raise ValueError(
                    f"Missing required fetch information for artifact '{artifact_key}'."
                )

            print(f"Processing artifact: {artifact_name}")
            print(f"Description: {description}")
            print(f"Downloading from: {url}")

            temp_file_path = temp_dir / artifact_name
            temp_file_download_path = temp_file_path.with_suffix(".download")

            download_file(url, temp_file_download_path)
            downloaded_files.append(temp_file_download_path)

            verify_sha256(temp_file_download_path, expected_sha)
            verify_size(temp_file_download_path, expected_size)

            temp_file_download_path.rename(temp_file_path)
            print(f"Successfully downloaded and verified: {artifact_name}\n")

        moz_fetches_dir = str(temp_dir)
        print("All artifacts downloaded and verified...")
        print("\n⏩ NEXT STEPS ⏩\n")
        print(
            "To export MOZ_FETCHES_DIR environment variable in your current shell, run the following command:\n"
        )

        if os.name == "nt":
            # Windows Command Prompt
            print(f"❯ set MOZ_FETCHES_DIR={moz_fetches_dir}")
            # Windows PowerShell
            print(f"❯ $env:MOZ_FETCHES_DIR={moz_fetches_dir}")
        else:
            # Linux/macOS
            print(f"❯ export MOZ_FETCHES_DIR={moz_fetches_dir}")

        print()

    except Exception as e:
        print(f"\nAn error occurred: {e}")
        print("Cleaning up downloaded files...")
        clean_up(temp_dir)
        sys.exit(1)


if __name__ == "__main__":
    main()
