# edpm/engine/generators/cmake_generator.py

import os
import json
from ruamel.yaml import YAML
from edpm.engine.config import ConfigNamespace
from edpm.engine.generators.steps import CmakeSet, CmakePrefixPath

class CmakeGenerator:
    def __init__(self, plan, lock, recipe_manager):
        self.plan = plan
        self.lock = lock
        self.recipe_manager = recipe_manager

    def build_toolchain_text(self) -> str:
        """Build CMake toolchain content with recipe-generated settings"""
        lines = [
            "# Automatically generated by EDPM\n",
            "# Do not edit by hand!\n\n"
        ]

        # Process global environment
        global_env_actions = self.plan.get_global_env_actions()
        for act in global_env_actions:
            if hasattr(act, 'gen_cmake_line'):
                lines.append(f"{act.gen_cmake_line()}\n")

        # Process installed packages
        for dep_name in sorted(self.lock.get_installed_packages()):
            dep_data = self.lock.get_installed_package(dep_name)
            ipath = dep_data.get("install_path", "")
            if not ipath or not os.path.isdir(ipath):
                continue

            # Get recipe-specific environment
            recipe_name = dep_name
            recipe_cls = self.recipe_manager.recipes_by_name.get(dep_name)
            recipe_actions = []
            if recipe_cls:
                recipe_actions = list(recipe_cls.gen_env(dep_data))

            # Get plan-defined environment
            dep_obj = self.plan.find_package(dep_name)
            plan_actions = dep_obj.env_block().parse({'install_dir': ipath}) if dep_obj else []

            # Combine and process all actions
            for act in recipe_actions + plan_actions:
                if hasattr(act, 'gen_cmake_line'):
                    lines.append(f"{act.gen_cmake_line()}\n")

        return "".join(lines)

    def save_toolchain_with_infile(self, in_file: str, out_file: str):
        """Merge EDPM content with existing CMake toolchain"""
        edpm_content = self.build_toolchain_text()

        if not in_file:
            self._write_text(out_file, edpm_content)
            return

        with open(in_file, 'r') as f:
            content = f.read()

        marker = "{{{EDPM-GENERATOR-CONTENT}}}"
        if marker in content:
            merged = content.replace(marker, edpm_content)
        else:
            merged = f"{content}\n{edpm_content}"

        self._write_text(out_file, merged)

    def build_presets_json(self) -> str:
        """Generate CMakePresets.json with recipe-specific settings"""
        preset = {
            "version": 3,
            "cmakeMinimumRequired": {"major": 3, "minor": 21},
            "configurePresets": [{
                "name": "edpm",
                "displayName": "EDPM-based config",
                "generator": "Unix Makefiles",
                "cacheVariables": {}
            }]
        }

        cache_vars = preset["configurePresets"][0]["cacheVariables"]
        cmake_vars = {}

        # Collect variables from all sources
        for package_name in sorted(self.lock.get_installed_packages()):
            dep_data = self.lock.get_installed_package(package_name)
            ipath = dep_data.get("install_path", "")
            if not ipath:
                continue

            # Recipe-generated variables
            recipe_name = self.recipe_manager.recipes_by_name.get(package_name)
            recipe_cls = self.recipe_manager.recipes_by_name.get(recipe_name)
            if recipe_cls:
                recipe = recipe_cls(config=ConfigNamespace(**dep_data.get('built_with_config', {})))
                for action in recipe.gen_env({'install_path': ipath}):
                    if isinstance(action, (CmakeSet, CmakePrefixPath)):
                        cmake_vars[action.name] = action.value

            # Plan-defined variables
            dep_obj = self.plan.find_package(package_name)
            if dep_obj:
                for action in dep_obj.env_block().parse({'install_dir': ipath}):
                    if isinstance(action, (CmakeSet, CmakePrefixPath)):
                        cmake_vars[action.name] = action.value

        # Format variables for CMakePresets
        for name, value in cmake_vars.items():
            cache_vars[name] = {"value": value, "type": "STRING"}

        return json.dumps(preset, indent=2)

    def save_presets_with_infile(self, in_file: str, out_file: str):
        """Merge EDPM presets with existing CMakePresets.json"""
        edpm_content = self.build_presets_json()

        if not in_file:
            self._write_text(out_file, edpm_content)
            return

        try:
            with open(in_file, 'r') as f:
                existing = json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            existing = {}

        # Merge presets
        edpm_preset = json.loads(edpm_content)
        existing.setdefault('configurePresets', []).extend(edpm_preset['configurePresets'])
        existing['version'] = edpm_preset['version']
        existing['cmakeMinimumRequired'] = edpm_preset['cmakeMinimumRequired']

        self._write_text(out_file, json.dumps(existing, indent=2))

    def _write_text(self, filename, text):
        """Utility method to write output files"""
        os.makedirs(os.path.dirname(filename), exist_ok=True)
        with open(filename, 'w') as f:
            f.write(text)