import logging
import os
from http import HTTPStatus
from re import fullmatch, match

import allure
import pytest
import requests
from frostfs_testlib import reporter
from frostfs_testlib.hosting import Hosting
from frostfs_testlib.resources.common import ASSETS_DIR
from frostfs_testlib.utils.env_utils import read_env_properties, save_env_properties
from frostfs_testlib.utils.version_utils import get_remote_binaries_versions
from pytest import FixtureRequest

logger = logging.getLogger("NeoLogger")


@allure.title("Check binaries versions")
@pytest.mark.check_binaries
def test_binaries_versions(request: FixtureRequest, hosting: Hosting):
    """
    Compare binaries versions from external source (url) and deployed on servers.
    """
    with reporter.step("Get binaries versions from servers"):
        got_versions, exсeptions_remote_binaries_versions = get_remote_binaries_versions(hosting)

    environment_dir = request.config.getoption("--alluredir") or ASSETS_DIR
    env_file = os.path.join(environment_dir, "environment.properties")
    env_properties = read_env_properties(env_file)

    # compare versions from servers and file
    exсeptions = []
    additional_env_properties = {}

    for binary_name, binary in got_versions.items():
        version = binary["version"]
        requires_check = binary["check"]
        if requires_check and not fullmatch(r"^\d+\.\d+\.\d+(-.*)?(?<!dirty)", version):
            exсeptions.append(f"{binary_name}: Actual version doesn't conform to format '0.0.0-000-aaaaaaa': {version}")

        # If some binary was not listed in the env properties file, let's add it
        # so that we have full information about versions in allure report
        if env_properties and binary_name not in env_properties:
            additional_env_properties[binary_name] = version

    if env_properties and additional_env_properties:
        save_env_properties(env_file, additional_env_properties)

    exсeptions.extend(exсeptions_remote_binaries_versions)

    # create clear beautiful error with aggregation info
    if exсeptions:
        msg = "\n".join(exсeptions)
        raise AssertionError(f"Found binaries with unexpected versions:\n{msg}")


@reporter.step("Download versions info from {url}")
def download_versions_info(url: str) -> dict:
    binaries_to_version = {}

    response = requests.get(url)

    assert response.status_code == HTTPStatus.OK, f"Got {response.status_code} code. Content {response.json()}"

    content = response.text
    assert content, f"Expected file with content, got {response}"

    for line in content.split("\n"):
        m = match("(.*)=(.*)", line)
        if not m:
            logger.warning(f"Could not get binary/version from {line}")
            continue
        bin_name, bin_version = m.group(1), m.group(2)
        binaries_to_version[bin_name] = bin_version

    return binaries_to_version