import sys

import pytest
from packaging.version import Version

from briefcase.exceptions import UnsupportedHostError
from briefcase.platforms.windows.app import WindowsAppCreateCommand


@pytest.fixture
def create_command(dummy_console, tmp_path):
    return WindowsAppCreateCommand(
        console=dummy_console,
        base_path=tmp_path / "base_path",
        data_path=tmp_path / "briefcase",
    )


@pytest.mark.parametrize("host_os", ["Darwin", "Linux", "WeirdOS"])
def test_unsupported_host_os(create_command, host_os):
    """Error raised for an unsupported OS."""
    create_command.tools.host_os = host_os

    with pytest.raises(
        UnsupportedHostError,
        match=r"Windows applications can only be built on Windows\.",
    ):
        create_command()


@pytest.mark.parametrize("host_arch", ["i686", "ARM64", "wonky"])
def test_unsupported_arch(create_command, host_arch, first_app_config):
    """Internal apps can only be developed on x86-64."""
    create_command.tools.host_os = "Windows"
    create_command.tools.host_arch = host_arch
    create_command.apps["first"] = first_app_config

    with pytest.raises(
        UnsupportedHostError,
        match=f"Windows applications cannot be built on an {host_arch} machine.",
    ):
        create_command.verify_host()


@pytest.mark.parametrize("host_arch", ["i686", "ARM64", "wonky"])
def test_unsupported_arch_external(create_command, host_arch, first_app_config, capsys):
    """External apps can be built on a different architecture, with a warning."""
    create_command.tools.host_os = "Windows"
    create_command.tools.host_arch = host_arch
    create_command.apps["first"] = first_app_config
    first_app_config.external_package_path = "external"

    create_command.verify_host()
    stdout, stderr = capsys.readouterr()
    assert "WARNING: Possible architecture mismatch" in stdout
    assert stderr == ""

    # The warning is not repeated on cloned commands.
    create_command.is_clone = True
    create_command.verify_host()
    assert capsys.readouterr() == ("", "")


def test_supported_arch(create_command):
    """Windows command are allowed to run on x86-64."""
    create_command.tools.host_os = "Windows"
    create_command.tools.host_arch = "AMD64"

    create_command.verify_host()


def test_unsupported_32bit_python(create_command):
    """Windows commands cannot run with 32bit Python."""
    create_command.tools.host_os = "Windows"
    create_command.tools.host_arch = "AMD64"
    create_command.tools.is_32bit_python = True

    with pytest.raises(
        UnsupportedHostError,
        match="Windows applications cannot be built using a 32bit version of Python",
    ):
        create_command()


def test_context(create_command, first_app_config):
    context = create_command.output_format_template_context(first_app_config)
    assert sorted(context.keys()) == [
        "binary_path",
        "guid",
        "install_scope",
        "package_path",
        "version_triple",
    ]


@pytest.mark.parametrize(
    ("version", "version_triple"),
    [
        ("1", "1.0.0"),
        ("1.2", "1.2.0"),
        ("1.2.3", "1.2.3"),
        ("1.2.3.4", "1.2.3"),
        ("1.2.3a4", "1.2.3"),
        ("1.2.3b5", "1.2.3"),
        ("1.2.3rc6", "1.2.3"),
        ("1.2.3.dev7", "1.2.3"),
        ("1.2.3.post8", "1.2.3"),
    ],
)
def test_version_triple(
    create_command, first_app_config, tmp_path, version, version_triple
):
    first_app_config.version = Version(version)
    context = create_command.output_format_template_context(first_app_config)

    assert context["version_triple"] == version_triple


def test_explicit_version_triple(create_command, first_app_config, tmp_path):
    first_app_config.version = "1.2.3a1"
    first_app_config.version_triple = "2.3.4"

    context = create_command.output_format_template_context(first_app_config)

    # Explicit version triple is used.
    assert context["version_triple"] == "2.3.4"


def test_guid(create_command, first_app_config, tmp_path):
    """A predictable GUID will be generated from the bundle."""
    context = create_command.output_format_template_context(first_app_config)

    assert context["guid"] == "d666a4f1-c7b7-52cc-888a-3a35a7cc97e5"


def test_explicit_guid(create_command, first_app_config, tmp_path):
    """If a GUID is explicitly provided, it is used."""
    first_app_config.guid = "e822176f-b755-589f-849c-6c6600f7efb1"
    context = create_command.output_format_template_context(first_app_config)

    # Explicitly provided GUID is used.
    assert context["guid"] == "e822176f-b755-589f-849c-6c6600f7efb1"


@pytest.mark.parametrize(
    ("revision", "micro"),
    [
        # Numerical revision
        (5, "5"),
        # Text revision
        ("5", "5"),
        # Pre-release revision
        ("0a3", "0"),
        ("0b2", "0"),
        ("0rc1", "0"),
    ],
)
def test_support_package_url(
    create_command, revision, micro, first_app_config, tmp_path
):
    """A valid support package URL is created for a support revision."""
    expected_link = (
        f"https://www.python.org/ftp/python"
        f"/{sys.version_info.major}.{sys.version_info.minor}.{micro}"
        f"/python-{sys.version_info.major}.{sys.version_info.minor}.{revision}-embed-amd64.zip"
    )
    assert create_command.support_package_url(revision) == expected_link


def test_default_install_scope(create_command, first_app_config, tmp_path):
    """By default, the installer gives a choice between per user and per machine."""
    context = create_command.output_format_template_context(first_app_config)
    assert context["install_scope"] == "perUserOrMachine"


def test_per_machine_install_scope(create_command, first_app_config, tmp_path):
    """App can be set to have explicit per-machine scope."""
    first_app_config.system_installer = True
    context = create_command.output_format_template_context(first_app_config)
    assert context["install_scope"] == "perMachine"


def test_per_user_install_scope(create_command, first_app_config, tmp_path):
    """App can be set to have explicit per-user scope."""
    first_app_config.system_installer = False
    context = create_command.output_format_template_context(first_app_config)
    assert context["install_scope"] == "perUser"


def test_package_path(create_command, first_app_config, tmp_path):
    """The default package_path is passed as an absolute path."""
    context = create_command.output_format_template_context(first_app_config)
    assert context["package_path"] == str(
        tmp_path / "base_path/build/first-app/windows/app/src"
    )


def test_binary_path(create_command, first_app_config, tmp_path):
    """The default binary_path is passed as a path relative to package_path."""
    context = create_command.output_format_template_context(first_app_config)
    assert context["binary_path"] == "First App.exe"


def test_external(create_command, external_first_app, tmp_path):
    """The package_path and binary_path can be overridden by the user."""
    context = create_command.output_format_template_context(external_first_app)
    assert context["package_path"] == str(tmp_path / "base_path/external/src")
    assert context["binary_path"] == "internal/app.exe"
