<filename>bmbf/anime_girls.py
"""Anime girls."""

from __future__ import annotations

from functools import reduce
import logging
from pathlib import Path
import random
from typing import Optional

logger = logging.getLogger(__name__)


class UnknownCategory(ValueError):
    """Unknown category error."""


class UnknownName(ValueError):
    """Unknown name error."""


class AnimeGirls:

    def __init__(self, root_path: Path) -> None:
        self.root_path = root_path
        self.girls: dict[str, set[Path]] = {}
        self.all_girls: set[Path] = set()
        self.reload()

    def reload(self):
        girls = {}
        for path in self.root_path.glob('*/*'):
            category = path.parent.name.lower()
            if category.startswith('.'):
                logger.debug('Skipping %r, bad category %r', path, category)
                continue
            if category not in girls:
                girls[category] = set()
            logger.debug('Adding %r, category %r', path, category)
            girls[category].add(Path(path))
        logger.info(
            'Loaded %d categories with %d anime girls holding books',
            len(girls), sum(len(v) for v in girls.values())
        )
        self.girls = girls
        self.all_girls = reduce(set.union, girls.values())

    def get(
            self,
            category: Optional[str] = None,
            name: Optional[str] = None,
    ) -> tuple[str, bytes]:
        # Limit to category, if provided
        if category is None:
            girls = self.all_girls
        else:
            try:
                girls = self.girls[category.lower()]
            except KeyError as e:
                raise UnknownCategory(category) from e

        # Limit to name, if provided
        if name is not None:
            for path in girls:
                if path.name.lower() == name.lower():
                    break
            else:
                raise UnknownName(name)
        else:
            path = random.choice(list(girls))

        # Load path
        logger.info('Loading %r', path)
        with path.open('rb') as f:
            return path.name, f.read()
