#!/usr/bin/python3


from __future__ import annotations

import json
import logging
import shutil
import sys
from pathlib import Path
from typing import Any

import tomlkit

from slpkg.config import config_load
from slpkg.repositories import Repositories

logger = logging.getLogger(__name__)


class RepoManager:  # pylint: disable=[R0902]
    """Enable, disable, and list repositories in repositories.toml."""

    def __init__(self) -> None:
        self.repos = Repositories()
        self.toml_file: Path = self.repos.repositories_toml_file

        self.cyan = config_load.cyan
        self.green = config_load.green
        self.red = config_load.red
        self.grey = config_load.grey
        self.bold = config_load.bold
        self.endc = config_load.endc

        self.columns = shutil.get_terminal_size().columns

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def list_repos(self, *, quiet: bool = False, option_json: bool = False) -> None:  # pylint: disable=[R0914]
        """Print a table of all repositories with their enabled/disabled status.

        Args:
            quiet:       When True, print only repository names one per line (no table).
            option_json: When True, output as a JSON array of repository objects.
        """
        repos = self.repos.repositories
        default = self.repos.default_repository

        if option_json:
            results = [
                {
                    "name": name,
                    "enabled": bool(cfg.get("enable")),
                    "default": name == default,
                    "mirror": cfg.get("mirror_packages", "") or cfg.get("mirror_changelog", ""),
                }
                for name, cfg in repos.items()
            ]
            print(json.dumps(results, indent=2))
            return

        if quiet:
            for name in repos:
                print(name)
            return

        name_width = max((len(r) for r in repos), default=10) + 4
        mirror_width = max(self.columns - name_width - 16, 20)

        print("Repositories:")
        print("=" * (self.columns - 1))
        print(
            f"{self.bold}{'Name:':<{name_width}}{'Status:':<14}{'Mirror:':<{mirror_width}}{self.endc}"
        )
        print("=" * (self.columns - 1))

        enabled_count = 0
        for name, cfg in repos.items():
            is_enabled: bool = bool(cfg.get("enable"))
            if is_enabled:
                enabled_count += 1
                status_color = self.green
                status = "Enabled"
            else:
                status_color = self.red
                status = "Disabled"

            mirror: str = cfg.get("mirror_packages", "") or cfg.get("mirror_changelog", "")
            if len(mirror) > mirror_width:
                mirror = mirror[: mirror_width - 3] + "..."

            display_name = f"{name} *" if name == default else name
            name_color = self.cyan if name == default else ""

            print(
                f"{name_color}{display_name:<{name_width}}{self.endc}"
                f"{status_color}{status:<14}{self.endc}"
                f"{mirror}"
            )

        print("=" * (self.columns - 1))
        print(
            f"{self.grey}{enabled_count}/{len(repos)} repositories enabled."
            f"  * Default: '{default}'.{self.endc}"
        )

    def enable(self, repo_names: list[str]) -> None:
        """Enable one or more repositories in repositories.toml.

        Args:
            repo_names: Repository names to enable (case-insensitive).
        """
        self._set_enable(repo_names, enabled=True)

    def disable(self, repo_names: list[str]) -> None:
        """Disable one or more repositories in repositories.toml.

        Args:
            repo_names: Repository names to disable (case-insensitive).
        """
        self._set_enable(repo_names, enabled=False)

    def set_default(self, repo_name: str) -> None:
        """Set the default repository in the [DEFAULT] section of repositories.toml.

        Args:
            repo_name: Repository name to set as default (case-insensitive).
        """
        known = {name.lower() for name in self.repos.repositories}
        name_lower = repo_name.lower()

        if name_lower not in known:
            print(f"{self.red}Error{self.endc}: repository '{repo_name}' not found in repositories.toml.")
            sys.exit(1)

        doc = self._load_toml()

        if "DEFAULT" not in doc or not isinstance(doc["DEFAULT"], dict):
            doc["DEFAULT"] = tomlkit.table()

        doc["DEFAULT"]["REPOSITORY"] = name_lower
        logger.info("Set DEFAULT REPOSITORY = %s", name_lower)
        self._write_toml(doc)
        print(f"{self.green}Default repository set{self.endc}: '{name_lower}'")

    def set_priority(self, repo_names: list[str]) -> None:
        """Set the [ALL_REPOS] PRIORITY list in repositories.toml.

        Args:
            repo_names: Ordered list of repository names for -o all mode.
                        Pass an empty list to clear the priority list.
        """
        known = {name.lower() for name in self.repos.repositories}
        normalized: list[str] = []
        unknown: list[str] = []

        for name in repo_names:
            name_lower = name.lower()
            if name_lower not in known:
                unknown.append(name)
            else:
                normalized.append(name_lower)

        if unknown:
            for u in unknown:
                print(f"{self.red}Error{self.endc}: repository '{u}' not found in repositories.toml.")
            sys.exit(1)

        doc = self._load_toml()

        if "ALL_REPOS" not in doc or not isinstance(doc["ALL_REPOS"], dict):
            doc["ALL_REPOS"] = tomlkit.table()

        priority_array = tomlkit.array()
        priority_array.extend(normalized)
        doc["ALL_REPOS"]["PRIORITY"] = priority_array

        logger.info("Set ALL_REPOS PRIORITY = %s", normalized)
        self._write_toml(doc)

        names_str = ", ".join(f"'{n}'" for n in normalized)
        print(f"{self.green}Priority set{self.endc}: [{names_str}]")

    def show_priority(self) -> None:
        """Print the current [ALL_REPOS] PRIORITY list."""
        doc = self._load_toml()
        priority: list[str] = []
        if "ALL_REPOS" in doc and isinstance(doc["ALL_REPOS"], dict):
            priority = list(doc["ALL_REPOS"].get("PRIORITY", []))

        if priority:
            names_str = ", ".join(f"'{n}'" for n in priority)
            print(f"{self.green}Priority{self.endc}: [{names_str}]")
        else:
            print(f"{self.green}Priority{self.endc}: (not set)")
        logger.info("Displayed priority list: %s", priority)

    def clear_priority(self) -> None:
        """Clear the [ALL_REPOS] PRIORITY list in repositories.toml."""
        doc = self._load_toml()
        if "ALL_REPOS" not in doc or not isinstance(doc["ALL_REPOS"], dict):
            doc["ALL_REPOS"] = tomlkit.table()

        priority_array = tomlkit.array()
        doc["ALL_REPOS"]["PRIORITY"] = priority_array

        logger.info("Cleared ALL_REPOS PRIORITY")
        self._write_toml(doc)
        print(f"{self.green}Priority cleared{self.endc}.")

    def set_mirror(self, repo_name: str, mirror_url: str) -> None:
        """Change the MIRROR URL for a repository in repositories.toml.

        Args:
            repo_name: Repository name (case-insensitive).
            mirror_url: New mirror URL to set.
        """
        known = {name.lower() for name in self.repos.repositories}
        name_lower = repo_name.lower()

        if name_lower not in known:
            print(f"{self.red}Error{self.endc}: repository '{repo_name}' not found in repositories.toml.")
            sys.exit(1)

        doc = self._load_toml()
        section_key = self._find_section_key(doc, name_lower)
        if section_key is None:
            print(f"{self.red}Error{self.endc}: section for '{repo_name}' not found in repositories.toml.")
            sys.exit(1)

        doc[section_key]["MIRROR"] = mirror_url
        logger.info("Set %s MIRROR = %s", section_key, mirror_url)
        self._write_toml(doc)
        print(f"{self.green}Mirror updated{self.endc}: '{name_lower}' → {mirror_url}")
        print("\nRun 'slpkg update' to refresh the package database.")

    # ------------------------------------------------------------------
    # Private helpers
    # ------------------------------------------------------------------

    def _find_section_key(self, doc: Any, name_lower: str) -> str | None:
        """Return the TOML section key whose lowercase matches name_lower, or None."""
        if name_lower.upper() in doc and isinstance(doc[name_lower.upper()], dict):
            return name_lower.upper()
        for key in doc:
            if str(key).lower() == name_lower and isinstance(doc[key], dict):
                return str(key)
        return None

    def _load_toml(self) -> Any:
        """Read repositories.toml preserving formatting via tomlkit."""
        try:
            return tomlkit.parse(self.toml_file.read_text(encoding="utf-8"))
        except FileNotFoundError:
            print(f"Error: repositories file not found: {self.toml_file}", file=sys.stderr)
            sys.exit(1)

    def _write_toml(self, doc: Any) -> None:
        """Write tomlkit document back to repositories.toml."""
        try:
            self.toml_file.write_text(tomlkit.dumps(doc), encoding="utf-8")
        except OSError as exc:
            print(f"Error writing {self.toml_file}: {exc}", file=sys.stderr)
            sys.exit(1)

    def _set_enable(self, repo_names: list[str], *, enabled: bool) -> None:  # pylint: disable=[R0914]
        """Set ENABLE = enabled for each named repository section."""
        known = {name.lower() for name in self.repos.repositories}
        doc = self._load_toml()

        changed: list[str] = []
        unknown: list[str] = []

        already: list[str] = []

        for name in repo_names:
            name_lower = name.lower()

            if name_lower not in known:
                unknown.append(name)
                continue

            section_key = self._find_section_key(doc, name_lower)
            if section_key is not None:
                current = bool(doc[section_key].get("ENABLE", True))
                if current == enabled:
                    already.append(name_lower)
                    continue
                doc[section_key]["ENABLE"] = enabled
                changed.append(name_lower)
                logger.info("Set %s ENABLE = %s", section_key, enabled)

        if unknown:
            for u in unknown:
                print(f"{self.red}Error{self.endc}: repository '{u}' not found in repositories.toml.")
            sys.exit(1)

        for name in already:
            state = "enabled" if enabled else "disabled"
            print(f"Already {state}: '{name}'")

        if changed:
            self._write_toml(doc)
            action = "Enabled" if enabled else "Disabled"
            color = self.green if enabled else self.red
            for name in changed:
                print(f"{color}{action}{self.endc}: '{name}'")
            if enabled:
                print("\nRun 'slpkg update' to refresh the package database.")
