import logging
from datetime import datetime
from pathlib import Path
from io import StringIO

import ruamel.yaml


logger = logging.getLogger(__name__)
parser = ruamel.yaml.YAML()


def join_yaml(
    extension_yaml_dir_path: Path,
    output_file_path: Path,
    force: bool = False,
) -> None:
    """Assemble YAML files into a single file."""
    logger.info("Assembling YAML files into a single file...")

    # Make sure extension.base.yaml file exists
    extension_base_yaml_file_path = extension_yaml_dir_path / "extension.base.yaml"
    if not (extension_base_yaml_file_path.exists() and extension_base_yaml_file_path.is_file()):
        logger.error(f"The {extension_base_yaml_file_path} file was not found")
        exit(1)

    with open(extension_base_yaml_file_path, "r") as extension_base_yaml_file:
        extension = parser.load(extension_base_yaml_file)

    # Assemble metrics
    assemble_metrics(extension, extension_yaml_dir_path)

    # Assemble datasource
    assemble_datasource(extension, "sqlPostgres", extension_yaml_dir_path)

    # Assemble topology
    assemble_topology(extension, extension_yaml_dir_path)

    # Assemble screens
    assemble_screens(extension, extension_yaml_dir_path)

    # Check if the output file exists and is was created manually
    if output_file_path.exists():
        with open(output_file_path, "r") as output_file:
            existing_output = output_file.read()
            if "generated by yamlex" not in existing_output and not force:
                logger.error(f"The {output_file_path} file was created manually. Use --force to overwrite it.")
                exit(2)

    # Convert dict to YAML. Dump to string first to add a comment
    stream = StringIO()
    parser.indent(mapping=2, sequence=4, offset=2)
    parser.dump(extension, stream)
    extension_yaml_text = stream.getvalue()

    # Write to output file
    with open(output_file_path, "w") as output_file:
        # Write a comment to indicate that the file was automatically generated
        comment = (
            "# This file was generated by yamlex\n"
            f"# at: {datetime.now().isoformat()}\n"
            "\n"
        )
        output_file.write(comment)
        output_file.write(extension_yaml_text)


def assemble_metrics(extension: dict, extension_yaml_dir_path: Path) -> None:
    metrics_dir_path = extension_yaml_dir_path / "metrics"
    metric_yaml_file_paths = list(metrics_dir_path.glob("**/*.yaml"))
    logger.info(f"Found {len(metric_yaml_file_paths)} metrics")

    if metric_yaml_file_paths:
        extension["metrics"] = []

    for metric_yaml_file_path in metric_yaml_file_paths:
        with open(metric_yaml_file_path, "r") as metric_yaml_file:
            metric = parser.load(metric_yaml_file)
            extension["metrics"].append(metric)


def assemble_datasource(extension: dict, name: str, extension_yaml_dir_path: Path) -> None:
    datasource_dir_path = extension_yaml_dir_path / name
    group_yaml_file_paths = list(datasource_dir_path.glob("*.yaml"))
    logger.info(f"Found {len(group_yaml_file_paths)} groups")

    if group_yaml_file_paths:
        extension[name] = []

    for group_yaml_file_path in group_yaml_file_paths:
        with open(group_yaml_file_path, "r") as group_yaml_file:
            group = parser.load(group_yaml_file)
            extension[name].append(group)


def assemble_topology(extension: dict, extension_yaml_dir_path: Path) -> None:
    types_dir_path = extension_yaml_dir_path / "topology" / "types"
    type_yaml_file_paths = list(types_dir_path.glob("*.yaml"))
    logger.info(f"Found {len(type_yaml_file_paths)} topology types")

    relationships_dir_path = extension_yaml_dir_path / "topology" / "relationships"
    relationship_yaml_file_paths = list(relationships_dir_path.glob("*.yaml"))
    logger.info(f"Found {len(relationship_yaml_file_paths)} topology relationships")

    if type_yaml_file_paths or relationship_yaml_file_paths:
        extension["topology"] = {}

    if type_yaml_file_paths:
        extension["topology"]["types"] = []

    for type_yaml_file_path in type_yaml_file_paths:
        with open(type_yaml_file_path, "r") as type_yaml_file:
            type_ = parser.load(type_yaml_file)
            extension["topology"]["types"].append(type_)

    if relationship_yaml_file_paths:
        extension["topology"]["relationships"] = []

    for relationship_yaml_file_path in relationship_yaml_file_paths:
        with open(relationship_yaml_file_path, "r") as relationship_yaml_file:
            relationship = parser.load(relationship_yaml_file)
            extension["topology"]["relationships"].append(relationship)


def assemble_screens(extension: dict, extension_yaml_dir_path: Path) -> None:
    screens_dir_path = extension_yaml_dir_path / "screens"
    screen_yaml_file_paths = list(screens_dir_path.glob("*.yaml"))
    logger.info(f"Found {len(screen_yaml_file_paths)} screens")

    if screen_yaml_file_paths:
        extension["screens"] = []

    for screen_yaml_file_path in screen_yaml_file_paths:
        with open(screen_yaml_file_path, "r") as screen_yaml_file:
            screen = parser.load(screen_yaml_file)
            extension["screens"].append(screen)
