#****************************************************************************
#* package_handler_skills.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 re
from typing import Dict, Optional, Tuple
from ..package import Package
from ..project_ops_info import ProjectUpdateInfo
from .package_handler import PackageHandler

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

# Frontmatter is delimited by lines containing only '---'
_FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
_FIELD_RE = re.compile(r"^(\w[\w-]*):\s*(.+)$", re.MULTILINE)


def _parse_frontmatter(path: str) -> Optional[Dict[str, str]]:
    """Return a dict of frontmatter fields, or None on failure."""
    try:
        with open(path) as fh:
            content = fh.read()
    except OSError as exc:
        _logger.warning("Could not read %s: %s", path, exc)
        return None

    m = _FRONTMATTER_RE.match(content)
    if not m:
        return None

    fields: Dict[str, str] = {}
    for fm in _FIELD_RE.finditer(m.group(1)):
        fields[fm.group(1)] = fm.group(2).strip()
    return fields


@dc.dataclass
class PackageHandlerSkills(PackageHandler):
    name = "skills"
    # package name -> (Package, skill filename, skill name, description, extra fields)
    skill_pkgs: Dict[str, Tuple] = dc.field(default_factory=dict)

    def process_pkg(self, pkg: Package):
        """Record packages that provide a SKILLS.md or SKILL.md file."""
        if not hasattr(pkg, "path") or pkg.path is None:
            return
        if getattr(pkg, "src_type", None) == "pypi":
            return

        for candidate in ("SKILLS.md", "SKILL.md"):
            skill_path = os.path.join(pkg.path, candidate)
            if os.path.isfile(skill_path):
                fields = _parse_frontmatter(skill_path)
                if fields is None:
                    _logger.warning(
                        "Package %s: %s has missing or malformed frontmatter; skipping",
                        pkg.name, candidate,
                    )
                    break
                skill_name = fields.get("name", "").strip()
                description = fields.get("description", "").strip()
                if not skill_name or not description:
                    _logger.warning(
                        "Package %s: %s frontmatter missing required 'name' or 'description'; skipping",
                        pkg.name, candidate,
                    )
                    break
                _logger.debug("Package %s has %s (skill: %s)", pkg.name, candidate, skill_name)
                self.skill_pkgs[pkg.name] = (pkg, candidate, skill_name, description, fields)
                break

    def update(self, update_info: ProjectUpdateInfo):
        if not self.skill_pkgs:
            _logger.debug("No packages with skill files; skipping SKILLS.md generation")
            return

        deps_dir = update_info.deps_dir
        output_path = os.path.join(deps_dir, "SKILLS.md")

        project_name = getattr(update_info, "project_name", None) or "project"

        _logger.debug("Writing SKILLS.md to %s", output_path)

        with open(output_path, "w") as fp:
            fp.write("---\n")
            fp.write("name: %s-skills\n" % project_name)
            fp.write("description: Aggregated agent skills from %s dependencies.\n" % project_name)
            fp.write("---\n\n")
            fp.write("<!-- Generated by IVPM — do not edit manually -->\n\n")
            fp.write("# Agent Skills\n\n")

            for pkg_name, (pkg, filename, skill_name, description, fields) in sorted(self.skill_pkgs.items()):
                fp.write("## %s\n\n" % skill_name)
                fp.write("%s\n\n" % description)

                # Propagate optional fields if present
                for field in ("license", "compatibility", "allowed-tools"):
                    val = fields.get(field, "").strip()
                    if val:
                        fp.write("**%s**: %s\n\n" % (field, val))

                fp.write("[Skill source](./%s/%s)\n\n" % (pkg_name, filename))

        from ..utils import note
        note("Generated SKILLS.md with %d skill(s)" % len(self.skill_pkgs))
