"""Read markdown files from the vault with YAML frontmatter parsing."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path

import frontmatter

from agntspace.vault.paths import VaultPaths


@dataclass
class VaultDocument:
    """A parsed markdown file from the vault."""

    path: Path
    metadata: dict = field(default_factory=dict)
    content: str = ""
    raw: str = ""


_IDENTITY_FILES = ("SOUL.md", "USER.md", "PROFILE.md", "CONTRACT.md", "TRUST.md", "RELATIONSHIP.md")  # arch:deterministic — structural list of the six identity files the reader hot-watches; vault layout invariant


class VaultReader:
    """Reads markdown files from the vault directory."""

    def __init__(self, vault_dir: Path, *, paths: VaultPaths | None = None) -> None:
        self._vault_dir = vault_dir
        self._paths = paths

    def _resolve(self, relative_path: str) -> Path:
        """Resolve a logical vault path to a physical path."""
        if self._paths:
            return self._paths.resolve(relative_path)
        return self._vault_dir / relative_path

    @property
    def vault_dir(self) -> Path:
        return self._vault_dir

    @property
    def paths(self) -> VaultPaths | None:
        return self._paths

    def resolve_identity_path(self, filename: str) -> str:
        """Resolve an identity file path."""
        return f"system/identity/{filename}"

    def read(self, relative_path: str) -> VaultDocument:
        """Read a single vault file. Raises FileNotFoundError if missing."""
        path = self._resolve(relative_path)
        if not path.is_file():
            raise FileNotFoundError(f"Vault file not found: {path}")
        raw = path.read_text(encoding="utf-8")
        post = frontmatter.loads(raw)
        return VaultDocument(
            path=path,
            metadata=dict(post.metadata),
            content=post.content,
            raw=raw,
        )

    def read_identity_files(self) -> dict[str, VaultDocument]:
        """Read the core identity files + MEMORY.

        Identity files (SOUL, USER, PROFILE, CONTRACT, TRUST) live in system/identity/.
        MEMORY.md lives in memory/.
        Returns a dict keyed by filename stem (e.g., 'SOUL', 'USER', 'MEMORY').
        """
        result: dict[str, VaultDocument] = {}
        for filename in _IDENTITY_FILES:
            path = self._resolve(f"system/identity/{filename}")
            if path.is_file():
                result[path.stem] = self.read(f"system/identity/{filename}")

        # MEMORY.md lives in memory/
        mem_path = self._resolve("memory/MEMORY.md")
        if mem_path.is_file():
            result["MEMORY"] = self.read("memory/MEMORY.md")

        return result

    # Directories that contain archived/versioned content — never included in listings.
    # Individual callers no longer need to filter these out.
    _SKIP_DIRS = frozenset((".history", ".archives", "_archives"))  # arch:deterministic — version-history and archive directories, never included in listings regardless of caller or user preference

    def list_files(self, directory: str = "", pattern: str = "*.md") -> list[Path]:
        """List markdown files in a vault subdirectory.

        Automatically excludes .history/, .archives/, and _archives/ directories
        so archived/versioned files never leak into knowledge pipelines.

        When VaultPaths is available, resolves the directory through the
        path resolver. When directory is empty, scans all content roots.
        """
        if self._paths:
            if directory:
                base = self._paths.resolve(directory)
                if not base.is_dir():
                    return []
                return sorted(self._filter_archived(base.glob(pattern)))
            # Empty directory = scan all content roots
            results: list[Path] = []
            for root in self._paths.all_content_roots():
                results.extend(self._filter_archived(root.glob(pattern)))
            return sorted(results)

        base = self._vault_dir / directory if directory else self._vault_dir
        if not base.is_dir():
            return []
        return sorted(self._filter_archived(base.glob(pattern)))

    @classmethod
    def _filter_archived(cls, paths) -> list[Path]:
        """Remove files inside archive/history directories."""
        return [
            p for p in paths
            if not cls._SKIP_DIRS.intersection(p.parts)
        ]

    def exists(self, relative_path: str) -> bool:
        """Check whether a vault file exists."""
        return self._resolve(relative_path).is_file()

    def dir_exists(self, relative_path: str) -> bool:
        """Check whether a vault directory exists."""
        return self._resolve(relative_path).is_dir()
