"""Scannt XDG-Dirs nach Foldern ohne klaren Paket-Owner."""
from __future__ import annotations

import os
import time
from pathlib import Path

from taninux.core.package_index import build_index
from taninux.core.state import State
from taninux.maintain.types import Category, Classification, Finding

# XDG-Dirs, die wir scannen. Reihenfolge = Anzeige-Reihenfolge.
XDG_DIRS = [
    Path.home() / ".config",
    Path.home() / ".local" / "share",
    Path.home() / ".cache",
    Path.home() / ".local" / "state",
]

# Schwellwerte (Tage)
SAFE_DAYS = 90
LIKELY_DAYS = 30

# Folders die nie als Findings erscheinen sollen (system-managed)
HARD_EXCLUDE = {
    "systemd", "dconf", "pulse", "pipewire", "wireplumber",
    "fontconfig", "gtk-3.0", "gtk-4.0", "Trash", "mimeapps.list",
    "user-dirs.dirs", "user-dirs.locale",
}

# Pfade die andere Scanner besser kennen — hier ausblenden um Duplikate zu vermeiden
OWNED_BY_OTHER_SCANNERS = {
    Path.home() / ".cache" / "paru",   # aur-cache
    Path.home() / ".cache" / "yay",    # aur-cache (fallback)
    Path.home() / ".var",              # flatpak-data (theoretisch — XDG-Dirs treffen das nicht, aber sicherheitshalber)
}


def _dir_size(path: Path) -> int:
    """Rekursive Grösse in Bytes. Symlinks werden nicht gefolgt."""
    total = 0
    try:
        for entry in os.scandir(path):
            try:
                if entry.is_symlink():
                    continue
                if entry.is_file(follow_symlinks=False):
                    total += entry.stat(follow_symlinks=False).st_size
                elif entry.is_dir(follow_symlinks=False):
                    total += _dir_size(Path(entry.path))
            except (OSError, PermissionError):
                continue
    except (OSError, PermissionError):
        pass
    return total


def _last_access_days(path: Path) -> int | None:
    """Tage seit letzter Modifikation (mtime). None wenn nicht lesbar."""
    try:
        mtime = path.stat().st_mtime
        return int((time.time() - mtime) / 86400)
    except OSError:
        return None


def _classify(matched_pkg: str | None, days: int | None) -> tuple[Classification, str]:
    if matched_pkg:
        return Classification.ACTIVE, f"Paket installiert: {matched_pkg}"
    if days is None:
        return Classification.UNKNOWN, "Zugriffszeit unbekannt"
    if days >= SAFE_DAYS:
        return Classification.SAFE, f"Kein Paket, {days} Tage inaktiv"
    if days >= LIKELY_DAYS:
        return Classification.LIKELY_ORPHAN, f"Kein Paket, {days} Tage inaktiv"
    return Classification.UNKNOWN, f"Kein Paket, aber nur {days} Tage inaktiv"


class DotfoldersScanner:
    name = "dotfolders"

    def __init__(self, state: State | None = None) -> None:
        self.state = state if state is not None else State.load()

    def scan(self) -> list[Finding]:
        idx = build_index()
        findings: list[Finding] = []

        for base in XDG_DIRS:
            if not base.is_dir():
                continue
            try:
                entries = list(os.scandir(base))
            except (OSError, PermissionError):
                continue

            for entry in entries:
                name = entry.name
                if name.startswith(".") or name in HARD_EXCLUDE:
                    continue
                path = Path(entry.path)
                if path in OWNED_BY_OTHER_SCANNERS:
                    continue
                if self.state.is_ignored(path):
                    continue

                matched = idx.matches(name)
                days = _last_access_days(path)

                if self.state.is_whitelisted(path):
                    classification = Classification.WHITELISTED
                    reason = "Auf der Whitelist"
                else:
                    classification, reason = _classify(matched, days)

                # Size nur für Cleanup-Kandidaten berechnen — ACTIVE/WHITELISTED
                # können riesig sein (Steam-Library!) und das Tool ist nicht
                # dafür da, installierte Apps zu vermessen.
                if classification in (Classification.ACTIVE, Classification.WHITELISTED):
                    size = 0
                elif path.is_dir():
                    size = _dir_size(path)
                else:
                    try:
                        size = path.stat().st_size
                    except OSError:
                        size = 0

                findings.append(Finding(
                    path=path,
                    category=Category.DOTFOLDERS,
                    classification=classification,
                    size_bytes=size,
                    last_access_days=days,
                    reason=reason,
                    matched_package=matched,
                ))

        findings.sort(key=lambda f: f.sort_key)
        return findings
