#****************************************************************************
#* package_handler_direnv.py
#*
#* Copyright 2024 Matthew Ballance and Contributors
#*
#* Licensed under the Apache License, Version 2.0 (the "License"); you may
#* not use this file except in compliance with the License.  
#* You may obtain a copy of the License at:
#*
#*   http://www.apache.org/licenses/LICENSE-2.0
#*
#* Unless required by applicable law or agreed to in writing, software
#* distributed under the License is distributed on an "AS IS" BASIS, 
#* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
#* See the License for the specific language governing permissions and
#* limitations under the License.
#*
#****************************************************************************
import dataclasses as dc
import logging
import os
import toposort
from typing import Dict
from ..package import Package
from ..project_ops_info import ProjectUpdateInfo
from .package_handler import PackageHandler

_logger = logging.getLogger("ivpm.handlers.package_handler_direnv")

@dc.dataclass
class PackageHandlerDirenv(PackageHandler):
    name = "direnv"
    # package name -> (Package, envrc filename)
    envrc_pkgs: Dict[str, tuple] = dc.field(default_factory=dict)

    def process_pkg(self, pkg: Package):
        """Record packages that provide an export.envrc or .envrc file."""
        if not hasattr(pkg, "path") or pkg.path is None:
            return
        # PyPI packages have no local directory
        if getattr(pkg, "src_type", None) == "pypi":
            return
        for candidate in ("export.envrc", ".envrc"):
            if os.path.isfile(os.path.join(pkg.path, candidate)):
                self.envrc_pkgs[pkg.name] = (pkg, candidate)
                _logger.debug("Package %s has %s", pkg.name, candidate)
                break

    def update(self, update_info: ProjectUpdateInfo):
        if not self.envrc_pkgs:
            _logger.debug("No packages with envrc files; skipping packages.envrc generation")
            return

        # Build dependency map for topological sort (only among envrc packages)
        deps_m: Dict[str, set] = {}
        for pkg_name, (pkg, _) in self.envrc_pkgs.items():
            if pkg_name not in deps_m:
                deps_m[pkg_name] = set()

            if pkg.proj_info is not None:
                target = pkg.proj_info.target_dep_set
                if pkg.proj_info.has_dep_set(target):
                    for dep_name in pkg.proj_info.get_dep_set(target).keys():
                        if dep_name in self.envrc_pkgs:
                            dep_pkg, _ = self.envrc_pkgs[dep_name]
                            # Only add edge if this package resolved the dependency
                            if dep_pkg.resolved_by == pkg_name:
                                deps_m[pkg_name].add(dep_name)

        # Topological order: dependencies before dependents
        ordered = []
        for pkg_set in toposort.toposort(deps_m):
            for pkg_name in pkg_set:
                ordered.append(pkg_name)

        deps_dir = update_info.deps_dir
        output_path = os.path.join(deps_dir, "packages.envrc")

        _logger.debug("Writing packages.envrc to %s", output_path)

        with open(output_path, "w") as fp:
            fp.write("# Generated by IVPM — do not edit manually\n")
            for pkg_name in ordered:
                _, envrc_file = self.envrc_pkgs[pkg_name]
                fp.write("source_env ./%s/%s\n" % (pkg_name, envrc_file))

        from ..utils import note
        note("Generated packages.envrc with %d entries" % len(ordered))
