"""Theming support for LaTeX builder."""

from __future__ import annotations

import configparser
from os import path
from typing import TYPE_CHECKING

from sphinx.errors import ThemeError
from sphinx.locale import __
from sphinx.util import logging

if TYPE_CHECKING:
    from sphinx.application import Sphinx
    from sphinx.config import Config

logger = logging.getLogger(__name__)


class Theme:
    """A set of LaTeX configurations."""

    LATEX_ELEMENTS_KEYS = ['papersize', 'pointsize']
    UPDATABLE_KEYS = ['papersize', 'pointsize']

    def __init__(self, name: str) -> None:
        self.name = name
        self.docclass = name
        self.wrapperclass = name
        self.papersize = 'letterpaper'
        self.pointsize = '10pt'
        self.toplevel_sectioning = 'chapter'

    def update(self, config: Config) -> None:
        """Override theme settings by user's configuration."""
        for key in self.LATEX_ELEMENTS_KEYS:
            if config.latex_elements.get(key):
                value = config.latex_elements[key]
                setattr(self, key, value)

        for key in self.UPDATABLE_KEYS:
            if key in config.latex_theme_options:
                value = config.latex_theme_options[key]
                setattr(self, key, value)


class BuiltInTheme(Theme):
    """A built-in LaTeX theme."""

    def __init__(self, name: str, config: Config) -> None:
        super().__init__(name)

        if name == 'howto':
            self.docclass = config.latex_docclass.get('howto', 'article')
        else:
            self.docclass = config.latex_docclass.get('manual', 'report')

        if name in ('manual', 'howto'):
            self.wrapperclass = 'sphinx' + name
        else:
            self.wrapperclass = name

        # we assume LaTeX class provides \chapter command except in case
        # of non-Japanese 'howto' case
        if name == 'howto' and not self.docclass.startswith('j'):
            self.toplevel_sectioning = 'section'
        else:
            self.toplevel_sectioning = 'chapter'


class UserTheme(Theme):
    """A user defined LaTeX theme."""

    REQUIRED_CONFIG_KEYS = ['docclass', 'wrapperclass']
    OPTIONAL_CONFIG_KEYS = ['papersize', 'pointsize', 'toplevel_sectioning']

    def __init__(self, name: str, filename: str) -> None:
        super().__init__(name)
        self.config = configparser.RawConfigParser()
        self.config.read(path.join(filename), encoding='utf-8')

        for key in self.REQUIRED_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoSectionError as exc:
                msg = __('%r doesn\'t have "theme" setting') % filename
                raise ThemeError(msg) from exc
            except configparser.NoOptionError as exc:
                msg = __('%r doesn\'t have "%s" setting') % (filename, exc.args[0])
                raise ThemeError(msg) from exc

        for key in self.OPTIONAL_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoOptionError:
                pass


class ThemeFactory:
    """A factory class for LaTeX Themes."""

    def __init__(self, app: Sphinx) -> None:
        self.themes: dict[str, Theme] = {}
        self.theme_paths = [
            path.join(app.srcdir, p) for p in app.config.latex_theme_path
        ]
        self.config = app.config
        self.load_builtin_themes(app.config)

    def load_builtin_themes(self, config: Config) -> None:
        """Load built-in themes."""
        self.themes['manual'] = BuiltInTheme('manual', config)
        self.themes['howto'] = BuiltInTheme('howto', config)

    def get(self, name: str) -> Theme:
        """Get a theme for given *name*."""
        if name in self.themes:
            theme = self.themes[name]
        else:
            theme = self.find_user_theme(name) or Theme(name)

        theme.update(self.config)
        return theme

    def find_user_theme(self, name: str) -> Theme | None:
        """Find a theme named as *name* from latex_theme_path."""
        for theme_path in self.theme_paths:
            config_path = path.join(theme_path, name, 'theme.conf')
            if path.isfile(config_path):
                try:
                    return UserTheme(name, config_path)
                except ThemeError as exc:
                    logger.warning(exc)

        return None
