import yaml
import subprocess
import os
import sys
import argparse
import shutil
from pathlib import Path

"""This file will take the loose requirements of a specific version and replace them with concrete version numbers.
Useage:
1. You should make the version number section you desire first in conadata.yml
2. Run this program
3. Repeat for alpha beta etc if desired.
"""


def main(conandata_file: Path, version: str, channel: str) -> None:
    """freeze the requirements section of a conandata.yml file"""

    requirements_latest = verify_requirements_file(conandata_file, version, channel)
    conaninfo_file = install_client(version, channel)
    print(f"Gathered conaninfo: {conaninfo_file}")

    full_req = parse_conaninfo(conaninfo_file)
    full_requirements = filter_requirements(full_req, requirements_latest)

    write_frozen_requirements(conandata_file, version, channel, full_requirements)

    spec = f"version={version}, channel={channel}"
    print(f"Updated conandata.yml to freeze section: {spec}")

    # cleanup
    shutil.rmtree("freezing")


def verify_requirements_file(file: Path, version: str, channel: str) -> list[str]:
    """Check that the requirements file has suitable layout and then return requirements 'loose' """
    with open(file, "r") as fid:
        data = yaml.safe_load(fid)

    if data.get("requirements") is None:
        raise ImportError(
            "To use conan freeze a 'requirements' section is required in the root of the conandata.yml file")

    if version not in data["requirements"]:
        raise ImportError(
            f"To use conan freeze the 'requirements' section must have a version number matching the input")

    if channel not in data["requirements"][version]:
        raise ImportError(
            f"To use conan freeze the 'channel' must be specified inside 'version'")

    return data["requirements"][version][channel]


def parse_conaninfo(file: Path) -> list[str]:
    """read a conan info file and return the full list of requirements with versions"""
    requires = []
    lines = file.read_text().splitlines()

    # Flag to indicate we are in the [requires] section
    in_requires_section = False

    for line in lines:
        # Check if we've reached the [requires] section
        if '[full_requires]' in line:
            in_requires_section = True
            continue  # Skip the line with the section header

        # Check for the end of the section or end of file
        if in_requires_section and (line.strip() == '' or line.startswith('[')):
            break

        # If we're in the [requires] section, add the dependency to the list
        if in_requires_section:
            # Strip the package ID
            dep = line.split(':')[0].strip()
            # Replace missing user/channel with '_/_'
            if '@' not in dep:
                dep += '@_/_'
            requires.append(dep)

    return requires


def install_client(version: str, channel: str) -> Path:
    """Install client to retrieve specific version numbers and then return path to conaninfo file"""
    tmp = Path("freezing")
    if tmp.exists():
        shutil.rmtree(tmp)  # want the error if it does not work
    tmp.mkdir()

    project = f"client/{version}@bigrep/{channel}"
    cmd = f"conan install --update -pr blade_release.jinja -if {tmp} -of {tmp} conanfile.py {project}"
    subprocess.run(cmd)

    # parse out the reuqirements
    conaninfo = tmp / "conaninfo.txt"
    return conaninfo


def filter_requirements(full_req: list[str], latest_req: list[str]) -> list[str]:
    """Keep only full requirements that are in the latest requirements list"""
    latest_names = [n.split("/")[0] for n in latest_req]
    full_names = [req for req in full_req if req.split("/")[0] in latest_names]
    return full_names


def write_frozen_requirements(file: Path, version: str, channel: str, full_req: list[str]) -> None:
    with open(file, 'r') as fid:
        data = yaml.safe_load(fid)

    data["requirements"][version][channel] = full_req

    with open(file, "w") as file:
        yaml.safe_dump(data, file, default_flow_style=False)


if __name__ == "__main__":
    description = "Tool to freeze a conandata file. This takes all references to latest and replaces them with the current real version"
    parser = argparse.ArgumentParser(
        description=description
    )
    parser.add_argument(
        "-v", "--version",
        help="The version to freeze name of the current version, e.g. 5.3.28 .",
        required=True
    )
    parser.add_argument(
        "-c", "--channel",
        help="The specific channel to freeze, e.g. stable.",
        required=True
    )
    parser.add_argument(
        "-p", "--path",
        help="Path to the conandata.yml file.",
        default="conandata.yml"
    )

    args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

    conandata_file = Path(args.path)
    version = args.version
    main(conandata_file, args.version, args.channel)
