# SPDX-FileCopyrightText: UL Research Institutes
# SPDX-License-Identifier: Apache-2.0


import logging
import re
from importlib import import_module
from io import StringIO
from pathlib import Path
from typing import Optional

from msgspec.structs import replace
from termcolor import colored

from cici.config.project import models as cici_config
from cici.config.project import serializers as cici_config_serializers

from ..constants import DEFAULT_PROVIDER
from ..paths import get_cici_config_path


def get_bundle_name(text):
    return re.sub(r"[-_ ]+", "-", text.casefold())


def get_bundle_content(provider, bundle, cici_config_file=None):
    content = StringIO()
    content.write(f"#\n")
    content.write(
        f"# This file is generated by cici-tools with the following command:\n"
    )
    content.write(f"#\n")
    content.write(f"#     cici bundle\n")
    content.write(f"#\n")
    provider.dump(
        bundle, content, cici_config_file=cici_config_file
    )  # where YAML is written
    return content.getvalue()


def load_cici_or_nothing(base_path: Path) -> Optional[cici_config.File]:
    # try to load and resolve .cici/config.yaml
    # - if config.yaml is there, load it, if not load nothing
    # - if config.yaml does not exist return None

    base_path = Path(base_path).resolve()

    if base_path.name == ".cici":
        base_path = base_path.parent

    config_path_root = base_path / ".cici" / "config.yaml"

    # load config.yaml if it exists
    config_path = None
    if config_path_root.exists():
        config_path = config_path_root

    if config_path:
        config_file = cici_config_serializers.load(config_path)
        # if load() succeeds then resove targets
        return cici_config_serializers.resolve_targets(config_file, config_path)

    return None


def bundle_command(parser, args):

    logging.basicConfig(level=logging.INFO, format="[%(levelname)s] %(message)s")

    provider = import_module(f".{DEFAULT_PROVIDER}", "cici.providers")

    cici_config_file = load_cici_or_nothing(args.config_path)

    if not cici_config_file:
        logging.warning(f"No config.yaml found in .cici")

    ci_file_path = args.config_path / provider.CI_FILE

    if not Path(ci_file_path).exists():
        parser.error(f"file not found: {ci_file_path}")

    file = provider.load(ci_file_path, cici_config_file=cici_config_file)

    args.output_path = Path(args.output_path)

    for job_name, job in file.jobs.items():
        if job_name.startswith("."):
            continue

        # replace deepcopy + direct mutation with msgspec replace
        bundle = replace(file, jobs={job_name: job})

        bundle_filename = args.output_path / f"{get_bundle_name(job_name)}.yml"
        content = get_bundle_content(
            provider, bundle, cici_config_file=cici_config_file
        )

        # ensure consistent whitespace
        content = "\n".join([line.rstrip() for line in content.splitlines()]) + "\n"

        if bundle_filename.exists() and bundle_filename.is_file():
            old_content = open(bundle_filename).read()
            if old_content == content:
                print("  ", colored(bundle_filename.name, "grey"))
                continue

        with open(bundle_filename, "w") as stream:
            stream.write(content)
        print(colored("⚡", "yellow"), bundle_filename.name)


def bundle_parser(subparsers):
    parser = subparsers.add_parser(
        "bundle", help="bundle CI jobs with dependencies into standalone distributions"
    )
    parser.add_argument(
        "config_path",
        metavar="DIR",
        nargs="?",
        type=Path,
        default=get_cici_config_path(),
    )
    parser.add_argument(
        "-o",
        "--output",
        metavar="DIR",
        dest="output_path",
        type=Path,
        default=Path.cwd().absolute(),
    )
    parser.set_defaults(func=bundle_command)
    return parser
