# File: src/noveler/infrastructure/adapters/file_episode_repository.py
# Purpose: Provide a filesystem-backed implementation of the episode repository port.
# Context: Used by infrastructure layer components that persist episodes locally.

"""Filesystem-based episode repository adapter with strict-mode aware fallback control."""

from __future__ import annotations

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

from noveler.domain.exceptions import RepositoryFallbackError
from noveler.infrastructure.config.strict_mode_config import StrictModeConfig
from noveler.infrastructure.ports.episode_repository import Episode, EpisodeRepository


class FileEpisodeRepository(EpisodeRepository):
    """Persist episodes as JSON files under a configured base directory.

    Args:
        base_dir: Explicit directory where episode files should be written.
        strict_config: Strict mode configuration that governs fallback behaviour.

    Raises:
        RepositoryFallbackError: Raised when strict repository mode forbids
            falling back to the default directory.
    """

    _REPOSITORY_NAME = "FileEpisodeRepository"

    def __init__(self, base_dir: Path | None = None, strict_config: StrictModeConfig | None = None) -> None:
        self._logger = logging.getLogger(__name__)
        self._strict_config = strict_config or StrictModeConfig.from_env()
        self.base_dir = self._resolve_base_dir(base_dir)
        self.base_dir.mkdir(parents=True, exist_ok=True)

    def _resolve_base_dir(self, base_dir: Path | None) -> Path:
        """Resolve the effective base directory, applying strict-mode rules."""
        if base_dir is not None:
            return base_dir

        if self._strict_config.is_repository_strict():
            raise RepositoryFallbackError(
                repository=self._REPOSITORY_NAME,
                attempted_fallback="default_directory",
                message="Base directory must be provided when repository strict mode is ERROR",
            )

        if self._strict_config.should_warn_on_repository_fallback():
            self._logger.warning(
                "[repository] Falling back to default episode directory because no base_dir was provided"
            )

        return Path.cwd() / "temp" / "ddd_repo"

    def _path(self, id: str) -> Path:
        return self.base_dir / f"{id}.json"

    def save(self, ep: Episode) -> None:
        """Persist the given episode to disk as a JSON payload."""
        p = self._path(ep.id)
        payload: dict[str, Any] = {"id": ep.id, "title": ep.title, "content": ep.content}
        p.write_text(json.dumps(payload, ensure_ascii=False), encoding="utf-8")

    def get(self, id: str) -> Episode | None:
        """Retrieve an episode by identifier, returning None when absent."""
        p = self._path(id)
        if not p.exists():
            return None
        data = json.loads(p.read_text(encoding="utf-8"))
        return Episode(**data)
