#!/usr/bin/python3
# -*- coding: utf-8 -*-

from __future__ import annotations

from pathlib import Path

from sbo_create.handlers.files import FileHandler
from sbo_create.models import AppContext


class SboResolver:
    """Single source of truth for SBo package data loading.

    Tries the local SBo git repository first (when SBO_REPO_PATH is
    configured), then falls back to the slackbuilds.json cache on every
    operation.  All handlers that need package metadata should call
    ``load()`` once and then read from ``ctx.sbo_packages``,
    ``ctx.sbo_versions`` and ``ctx.sbo_requires``.
    """

    def __init__(self, ctx: AppContext, files: FileHandler) -> None:
        self.ctx = ctx
        self.files = files

    def load(self) -> None:
        """Populate ctx caches: local repo first, slackbuilds.json as fallback.

        No-op when the caches are already populated.
        """
        if self.ctx.sbo_packages:
            return
        if self.ctx.maintainer.sbo_repo_path:
            self._load_from_local_repo()
        if not self.ctx.sbo_packages:
            self.files.fetch_sbo_packages()

    def find_pkg_dir(self, name: str) -> Path | None:
        """Return the package directory inside the local SBo repo, or None.

        Scans top-level category directories of the configured repo root and
        returns the first matching package directory.  Returns None when no
        local repo is configured or the package is not found.

        Args:
            name: Exact SBo package name to locate.
        """
        repo_path_str: str = self.ctx.maintainer.sbo_repo_path
        if not repo_path_str:
            return None
        repo_root: Path = Path(repo_path_str).expanduser()
        if not repo_root.is_dir():
            return None
        for cat_dir in sorted(repo_root.iterdir()):
            if cat_dir.is_dir() and (cat_dir / name).is_dir():
                return cat_dir / name
        return None

    def _load_from_local_repo(self) -> None:
        """Scan all .info files in the local SBo repo and populate ctx caches."""
        repo_root: Path = Path(self.ctx.maintainer.sbo_repo_path).expanduser()
        if not repo_root.is_dir():
            return
        for info_file in sorted(repo_root.rglob('*.info')):
            self._parse_info_file(info_file, repo_root)

    def _parse_info_file(self, info_file: Path, repo_root: Path) -> None:
        """Parse a single .info file and store the result in ctx caches.

        Extracts category (from the directory structure), VERSION and REQUIRES
        from the .info file.  Short description is not available in .info files
        so it is stored as an empty string; all callers rely only on the
        category part.

        Args:
            info_file: Absolute path to the .info file.
            repo_root: Absolute path to the root of the local SBo repository,
                used to derive the category from the relative path.
        """
        parts: tuple[str, ...] = info_file.relative_to(repo_root).parts
        if len(parts) < 2:
            return
        category: str = parts[0]
        name: str = parts[1]
        fields: dict[str, str] = {}
        for line in info_file.read_text(encoding='utf-8').splitlines():
            if '=' in line:
                k, _, v = line.partition('=')
                fields[k.strip()] = v.strip().strip('"')
        self.ctx.sbo_packages[name] = f'{category}  '
        self.ctx.sbo_versions[name] = fields.get('VERSION', '')
        requires: str = fields.get('REQUIRES', '')
        if requires:
            self.ctx.sbo_requires[name] = requires.split()
