import os
import re
from argparse import Namespace
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional

import yaml
from webmentions import WebmentionStatus


@dataclass
class Config:
    """
    Configuration for the blog
    """

    title: str = "Blog"
    description: str = ""
    link: str = "/"
    home_link: str = "/"
    external_links: List[str] = field(default_factory=list)
    host: str = "0.0.0.0"
    port: int = 8000
    language: str = "en-US"
    logo: str = "/img/icon.png"
    header: bool = True
    content_dir: str = "."
    categories: List[str] = field(default_factory=list)
    short_feed: bool = False
    max_entries_per_feed: int = 10
    enable_webmentions: bool = True
    webmentions_hard_delete: bool = False
    default_webmention_status: WebmentionStatus = WebmentionStatus.CONFIRMED
    debug: bool = False
    basedir = os.path.abspath(os.path.dirname(__file__))
    author: str | None = None
    author_url: str | None = None
    author_photo: str | None = None
    throttle_seconds_on_update: int = 10
    author_email: str | None = None
    smtp_server: str | None = None
    smtp_port: int = 587
    smtp_username: str | None = None
    smtp_password: str | None = None
    smtp_starttls: bool = True
    smtp_enable_starttls_auto: bool = True
    smtp_sender: str | None = None
    view_mode: str = "cards"  # "cards", "list", or "full"
    external_feeds: List[str] = field(default_factory=list)
    external_feeds_as_folders: bool = False
    feeds_cache_expiry_secs: int = 300

    # ActivityPub
    enable_activitypub: bool = False
    activitypub_link: str | None = None
    activitypub_username: str = "blog"
    activitypub_domain: str | None = None
    activitypub_name: str | None = None
    activitypub_summary: str | None = None
    activitypub_icon_url: str | None = None
    activitypub_profile_field_name: str = "Blog"
    activitypub_profile_fields: Dict[str, str] = field(default_factory=dict)
    activitypub_private_key_path: str | None = None
    activitypub_manually_approves_followers: bool = False
    activitypub_description_only: bool = False
    activitypub_object_type: str = "Note"
    activitypub_posts_content_wrapped: bool = False
    activitypub_email_notifications: bool = True
    activitypub_quote_control: str = "public"
    activitypub_auto_approve_quotes: bool = True

    # Guestbook
    enable_guestbook: bool = True

    # Rendering features
    enable_latex: bool = True
    enable_mermaid: bool = True

    # Moderation
    blocked_actors: List[str] = field(default_factory=list)
    allowed_actors: List[str] = field(default_factory=list)

    # State directory
    state_dir: str | None = None  # None means use default: <content_dir>/.madblog

    # Visibility
    default_visibility: str = "public"

    @property
    def resolved_state_dir(self) -> Path:
        """Return the resolved state directory path."""
        if self.state_dir:
            return Path(self.state_dir).expanduser().resolve()
        return Path(self.content_dir).expanduser().resolve() / ".madblog"

    @property
    def templates_dir(self) -> str:
        return os.path.join(self.basedir, "templates")

    @property
    def static_dir(self) -> str:
        return os.path.join(self.basedir, "static")

    @property
    def default_css_dir(self) -> str:
        return os.path.join(self.static_dir, "css")

    @property
    def default_js_dir(self) -> str:
        return os.path.join(self.static_dir, "js")

    @property
    def default_fonts_dir(self) -> str:
        return os.path.join(self.static_dir, "fonts")

    @property
    def default_img_dir(self) -> str:
        return os.path.join(self.static_dir, "img")

    @property
    def activitypub_profile_url(self) -> Optional[str]:
        if not self.enable_activitypub:
            return None

        base = self.link.rstrip("/")
        return f"{base}/@{self.activitypub_username}"

    @property
    def activitypub_actor_url(self) -> Optional[str]:
        if not self.enable_activitypub:
            return None

        base = (self.activitypub_link or self.link).rstrip("/")
        return f"{base}/ap/actor"

    @property
    def webmention_url(self) -> Optional[str]:
        from flask import request

        if not self.enable_webmentions:
            return None

        return (
            f'{self.link.rstrip("/")}/webmentions'
            if re.match(r"^https?://", self.link)
            else f'{request.host_url.rstrip("/")} /webmentions'
        )


config = Config()


def _init_config_from_file(  # pylint: disable=too-many-branches,too-many-statements
    config_file: str,
):
    cfg = {}

    if os.path.isfile(config_file):
        with open(config_file, "r") as f:
            cfg = yaml.safe_load(f) or {}

    if cfg.get("content_dir"):
        config.content_dir = cfg["content_dir"]
    if cfg.get("title"):
        config.title = cfg["title"]
    if cfg.get("description"):
        config.description = cfg["description"]
    if cfg.get("link"):
        config.link = cfg["link"]
    if cfg.get("home_link"):
        config.home_link = cfg["home_link"]
    if cfg.get("external_links"):
        config.external_links = cfg["external_links"]
    if cfg.get("host"):
        config.host = cfg["host"]
    if cfg.get("port"):
        config.port = int(cfg["port"])
    if cfg.get("logo") is not None:
        config.logo = cfg["logo"]
    if cfg.get("language"):
        config.language = cfg["language"]
    if cfg.get("header"):
        config.header = bool(cfg["header"])
    if cfg.get("short_feed"):
        config.short_feed = bool(cfg["short_feed"])
    if cfg.get("max_entries_per_feed") is not None:
        config.max_entries_per_feed = int(cfg["max_entries_per_feed"])
    if cfg.get("enable_webmentions") is not None:
        config.enable_webmentions = bool(cfg["enable_webmentions"])
    if cfg.get("webmentions_hard_delete") is not None:
        config.webmentions_hard_delete = bool(cfg["webmentions_hard_delete"])
    if cfg.get("default_webmention_status"):
        config.default_webmention_status = WebmentionStatus(
            cfg["default_webmention_status"]
        )
    if cfg.get("debug") is not None:
        config.debug = bool(cfg["debug"])
    if cfg.get("author"):
        config.author = cfg["author"]
    if cfg.get("author_url"):
        config.author_url = cfg["author_url"]
    if cfg.get("author_photo"):
        config.author_photo = cfg["author_photo"]
    if cfg.get("throttle_seconds_on_update"):
        config.throttle_seconds_on_update = int(cfg["throttle_seconds_on_update"])
    if cfg.get("author_email"):
        config.author_email = cfg["author_email"]
    if cfg.get("smtp_server"):
        config.smtp_server = cfg["smtp_server"]
    if cfg.get("smtp_port"):
        config.smtp_port = int(cfg["smtp_port"])
    if cfg.get("smtp_username"):
        config.smtp_username = cfg["smtp_username"]
    if cfg.get("smtp_password"):
        config.smtp_password = cfg["smtp_password"]
    if cfg.get("smtp_starttls") is not None:
        config.smtp_starttls = bool(cfg["smtp_starttls"])
    if cfg.get("smtp_enable_starttls_auto") is not None:
        config.smtp_enable_starttls_auto = bool(cfg["smtp_enable_starttls_auto"])
    if cfg.get("smtp_sender"):
        config.smtp_sender = cfg["smtp_sender"]
    if cfg.get("view_mode"):
        config.view_mode = cfg["view_mode"]
    if cfg.get("external_feeds"):
        config.external_feeds = cfg["external_feeds"]
    if cfg.get("external_feeds_as_folders") is not None:
        config.external_feeds_as_folders = bool(cfg["external_feeds_as_folders"])
    if cfg.get("feeds_cache_expiry_secs"):
        config.feeds_cache_expiry_secs = int(cfg["feeds_cache_expiry_secs"])
    config.categories = cfg.get("categories", [])

    # ActivityPub
    if cfg.get("enable_activitypub") is not None:
        config.enable_activitypub = bool(cfg["enable_activitypub"])
    if cfg.get("activitypub_link"):
        config.activitypub_link = cfg["activitypub_link"]
    if cfg.get("activitypub_username"):
        config.activitypub_username = cfg["activitypub_username"]
    if cfg.get("activitypub_domain"):
        config.activitypub_domain = cfg["activitypub_domain"]
    if cfg.get("activitypub_name"):
        config.activitypub_name = cfg["activitypub_name"]
    if cfg.get("activitypub_summary"):
        config.activitypub_summary = cfg["activitypub_summary"]
    if cfg.get("activitypub_icon_url"):
        config.activitypub_icon_url = cfg["activitypub_icon_url"]
    if cfg.get("activitypub_profile_field_name"):
        config.activitypub_profile_field_name = str(
            cfg["activitypub_profile_field_name"]
        )

    if cfg.get("activitypub_profile_fields") is not None:
        fields = cfg["activitypub_profile_fields"]
        if isinstance(fields, dict):
            config.activitypub_profile_fields = {
                str(k): str(v)
                for k, v in fields.items()
                if k is not None and v is not None
            }
        else:
            raise ValueError(
                "activitypub_profile_fields must be a mapping of name->value"
            )
    if cfg.get("activitypub_private_key_path"):
        config.activitypub_private_key_path = cfg["activitypub_private_key_path"]
    if cfg.get("activitypub_manually_approves_followers") is not None:
        config.activitypub_manually_approves_followers = bool(
            cfg["activitypub_manually_approves_followers"]
        )
    if cfg.get("activitypub_description_only") is not None:
        config.activitypub_description_only = bool(cfg["activitypub_description_only"])
    if cfg.get("activitypub_object_type"):
        config.activitypub_object_type = cfg["activitypub_object_type"]
    if cfg.get("activitypub_posts_content_wrapped") is not None:
        config.activitypub_posts_content_wrapped = bool(
            cfg["activitypub_posts_content_wrapped"]
        )
    if cfg.get("activitypub_email_notifications") is not None:
        config.activitypub_email_notifications = bool(
            cfg["activitypub_email_notifications"]
        )
    if cfg.get("activitypub_quote_control"):
        config.activitypub_quote_control = cfg["activitypub_quote_control"]
    if cfg.get("activitypub_auto_approve_quotes") is not None:
        config.activitypub_auto_approve_quotes = bool(
            cfg["activitypub_auto_approve_quotes"]
        )
    config.categories = cfg.get("categories", [])

    # Guestbook
    if cfg.get("enable_guestbook") is not None:
        config.enable_guestbook = bool(cfg["enable_guestbook"])

    # Rendering features
    if cfg.get("enable_latex") is not None:
        config.enable_latex = bool(cfg["enable_latex"])
    if cfg.get("enable_mermaid") is not None:
        config.enable_mermaid = bool(cfg["enable_mermaid"])

    # Moderation
    if cfg.get("blocked_actors"):
        config.blocked_actors = list(cfg["blocked_actors"])
    if cfg.get("allowed_actors"):
        config.allowed_actors = list(cfg["allowed_actors"])

    # State directory
    if cfg.get("state_dir"):
        config.state_dir = cfg["state_dir"]

    # Visibility
    if cfg.get("default_visibility"):
        config.default_visibility = cfg["default_visibility"]


def _init_config_from_env():  # pylint: disable=too-many-branches,too-many-statements
    if os.getenv("MADBLOG_TITLE"):
        config.title = os.environ["MADBLOG_TITLE"]
    if os.getenv("MADBLOG_DESCRIPTION"):
        config.description = os.environ["MADBLOG_DESCRIPTION"]
    if os.getenv("MADBLOG_LINK"):
        config.link = os.environ["MADBLOG_LINK"]
    if os.getenv("MADBLOG_HOME_LINK"):
        config.home_link = os.environ["MADBLOG_HOME_LINK"]
    if os.getenv("MADBLOG_EXTERNAL_LINKS"):
        config.external_links = re.split(
            r"[,\s]+", os.environ["MADBLOG_EXTERNAL_LINKS"].strip()
        )
    if os.getenv("MADBLOG_HOST"):
        config.host = os.environ["MADBLOG_HOST"]
    if os.getenv("MADBLOG_PORT"):
        config.port = int(os.environ["MADBLOG_PORT"])
    if os.getenv("MADBLOG_CONTENT_DIR"):
        config.content_dir = os.environ["MADBLOG_CONTENT_DIR"]
    if os.getenv("MADBLOG_LOGO"):
        config.logo = os.environ["MADBLOG_LOGO"]
    if os.getenv("MADBLOG_LANGUAGE"):
        config.language = os.environ["MADBLOG_LANGUAGE"]
    if os.getenv("MADBLOG_HEADER"):
        config.header = os.environ["MADBLOG_HEADER"] == "1"
    if os.getenv("MADBLOG_SHORT_FEED"):
        config.short_feed = os.environ["MADBLOG_SHORT_FEED"] == "1"
    if os.getenv("MADBLOG_MAX_ENTRIES_PER_FEED"):
        config.max_entries_per_feed = int(os.environ["MADBLOG_MAX_ENTRIES_PER_FEED"])
    if os.getenv("MADBLOG_ENABLE_WEBMENTIONS"):
        config.enable_webmentions = os.environ["MADBLOG_ENABLE_WEBMENTIONS"] == "1"
    if os.getenv("MADBLOG_WEBMENTIONS_HARD_DELETE"):
        config.webmentions_hard_delete = (
            os.environ["MADBLOG_WEBMENTIONS_HARD_DELETE"] == "1"
        )
    if os.getenv("MADBLOG_DEFAULT_WEBMENTION_STATUS"):
        config.default_webmention_status = WebmentionStatus(
            os.environ["MADBLOG_DEFAULT_WEBMENTION_STATUS"]
        )
    if os.getenv("MADBLOG_DEBUG"):
        config.debug = os.environ["MADBLOG_DEBUG"] == "1"
    if os.getenv("MADBLOG_VIEW_MODE"):
        config.view_mode = os.environ["MADBLOG_VIEW_MODE"]
    if os.getenv("MADBLOG_CATEGORIES"):
        config.categories = re.split(r"[,\s]+", os.environ["MADBLOG_CATEGORIES"])
    if os.getenv("MADBLOG_AUTHOR"):
        config.author = os.environ["MADBLOG_AUTHOR"]
    if os.getenv("MADBLOG_AUTHOR_URL"):
        config.author_url = os.environ["MADBLOG_AUTHOR_URL"]
    if os.getenv("MADBLOG_AUTHOR_PHOTO"):
        config.author_photo = os.environ["MADBLOG_AUTHOR_PHOTO"]
    if os.getenv("MADBLOG_THROTTLE_SECONDS_ON_UPDATE"):
        config.throttle_seconds_on_update = int(
            os.environ["MADBLOG_THROTTLE_SECONDS_ON_UPDATE"]
        )
    if os.getenv("MADBLOG_AUTHOR_EMAIL"):
        config.author_email = os.environ["MADBLOG_AUTHOR_EMAIL"]
    if os.getenv("MADBLOG_SMTP_SERVER"):
        config.smtp_server = os.environ["MADBLOG_SMTP_SERVER"]
    if os.getenv("MADBLOG_SMTP_PORT"):
        config.smtp_port = int(os.environ["MADBLOG_SMTP_PORT"])
    if os.getenv("MADBLOG_SMTP_USERNAME"):
        config.smtp_username = os.environ["MADBLOG_SMTP_USERNAME"]
    if os.getenv("MADBLOG_SMTP_PASSWORD"):
        config.smtp_password = os.environ["MADBLOG_SMTP_PASSWORD"]
    if os.getenv("MADBLOG_SMTP_STARTTLS"):
        config.smtp_starttls = os.environ["MADBLOG_SMTP_STARTTLS"] == "1"
    if os.getenv("MADBLOG_SMTP_ENABLE_STARTTLS_AUTO"):
        config.smtp_enable_starttls_auto = (
            os.environ["MADBLOG_SMTP_ENABLE_STARTTLS_AUTO"] == "1"
        )
    if os.getenv("MADBLOG_SMTP_SENDER"):
        config.smtp_sender = os.environ["MADBLOG_SMTP_SENDER"]
    if os.getenv("MADBLOG_EXTERNAL_FEEDS"):
        config.external_feeds = re.split(
            r"[,\s]+", os.environ["MADBLOG_EXTERNAL_FEEDS"].strip()
        )
    if os.getenv("MADBLOG_EXTERNAL_FEEDS_AS_FOLDERS"):
        config.external_feeds_as_folders = os.environ[
            "MADBLOG_EXTERNAL_FEEDS_AS_FOLDERS"
        ].lower() in ("1", "true", "yes")
    if os.getenv("MADBLOG_FEEDS_CACHE_EXPIRY_SECS"):
        config.feeds_cache_expiry_secs = int(
            os.environ["MADBLOG_FEEDS_CACHE_EXPIRY_SECS"]
        )

    # ActivityPub
    if os.getenv("MADBLOG_ENABLE_ACTIVITYPUB"):
        config.enable_activitypub = os.environ["MADBLOG_ENABLE_ACTIVITYPUB"] == "1"
    if os.getenv("MADBLOG_ACTIVITYPUB_LINK"):
        config.activitypub_link = os.environ["MADBLOG_ACTIVITYPUB_LINK"]
    if os.getenv("MADBLOG_ACTIVITYPUB_USERNAME"):
        config.activitypub_username = os.environ["MADBLOG_ACTIVITYPUB_USERNAME"]
    if os.getenv("MADBLOG_ACTIVITYPUB_DOMAIN"):
        config.activitypub_domain = os.environ["MADBLOG_ACTIVITYPUB_DOMAIN"]
    if os.getenv("MADBLOG_ACTIVITYPUB_NAME"):
        config.activitypub_name = os.environ["MADBLOG_ACTIVITYPUB_NAME"]
    if os.getenv("MADBLOG_ACTIVITYPUB_SUMMARY"):
        config.activitypub_summary = os.environ["MADBLOG_ACTIVITYPUB_SUMMARY"]
    if os.getenv("MADBLOG_ACTIVITYPUB_ICON_URL"):
        config.activitypub_icon_url = os.environ["MADBLOG_ACTIVITYPUB_ICON_URL"]
    if os.getenv("MADBLOG_ACTIVITYPUB_PRIVATE_KEY_PATH"):
        config.activitypub_private_key_path = os.environ[
            "MADBLOG_ACTIVITYPUB_PRIVATE_KEY_PATH"
        ]
    if os.getenv("MADBLOG_ACTIVITYPUB_MANUALLY_APPROVES_FOLLOWERS"):
        config.activitypub_manually_approves_followers = (
            os.environ["MADBLOG_ACTIVITYPUB_MANUALLY_APPROVES_FOLLOWERS"] == "1"
        )
    if os.getenv("MADBLOG_ACTIVITYPUB_DESCRIPTION_ONLY"):
        config.activitypub_description_only = (
            os.environ["MADBLOG_ACTIVITYPUB_DESCRIPTION_ONLY"] == "1"
        )
    if os.getenv("MADBLOG_ACTIVITYPUB_OBJECT_TYPE"):
        config.activitypub_object_type = os.environ["MADBLOG_ACTIVITYPUB_OBJECT_TYPE"]
    if os.getenv("MADBLOG_ACTIVITYPUB_POSTS_CONTENT_WRAPPED"):
        config.activitypub_posts_content_wrapped = (
            os.environ["MADBLOG_ACTIVITYPUB_POSTS_CONTENT_WRAPPED"] == "1"
        )
    if os.getenv("MADBLOG_ACTIVITYPUB_EMAIL_NOTIFICATIONS"):
        config.activitypub_email_notifications = (
            os.environ["MADBLOG_ACTIVITYPUB_EMAIL_NOTIFICATIONS"] == "1"
        )
    if os.getenv("MADBLOG_ACTIVITYPUB_QUOTE_CONTROL"):
        config.activitypub_quote_control = os.environ[
            "MADBLOG_ACTIVITYPUB_QUOTE_CONTROL"
        ]
    if os.getenv("MADBLOG_ACTIVITYPUB_AUTO_APPROVE_QUOTES"):
        config.activitypub_auto_approve_quotes = (
            os.environ["MADBLOG_ACTIVITYPUB_AUTO_APPROVE_QUOTES"] == "1"
        )
    # Guestbook
    if os.getenv("MADBLOG_ENABLE_GUESTBOOK"):
        config.enable_guestbook = os.environ["MADBLOG_ENABLE_GUESTBOOK"] == "1"

    # Rendering features
    if os.getenv("MADBLOG_ENABLE_LATEX"):
        config.enable_latex = os.environ["MADBLOG_ENABLE_LATEX"] == "1"
    if os.getenv("MADBLOG_ENABLE_MERMAID"):
        config.enable_mermaid = os.environ["MADBLOG_ENABLE_MERMAID"] == "1"

    # Moderation
    if os.getenv("MADBLOG_BLOCKED_ACTORS"):
        config.blocked_actors = re.split(
            r"[,\s]+", os.environ["MADBLOG_BLOCKED_ACTORS"].strip()
        )
    if os.getenv("MADBLOG_ALLOWED_ACTORS"):
        config.allowed_actors = re.split(
            r"[,\s]+", os.environ["MADBLOG_ALLOWED_ACTORS"].strip()
        )

    if os.getenv("MADBLOG_ACTIVITYPUB_PROFILE_FIELD_NAME"):
        config.activitypub_profile_field_name = os.environ[
            "MADBLOG_ACTIVITYPUB_PROFILE_FIELD_NAME"
        ]

    # State directory
    if os.getenv("MADBLOG_STATE_DIR"):
        config.state_dir = os.environ["MADBLOG_STATE_DIR"]

    # Visibility
    if os.getenv("MADBLOG_DEFAULT_VISIBILITY"):
        config.default_visibility = os.environ["MADBLOG_DEFAULT_VISIBILITY"]


def _init_config_from_cli(args: Optional[Namespace]):
    if not args:
        return

    if args.dir:
        config.content_dir = args.dir
    if args.host:
        config.host = args.host
    if args.port:
        config.port = args.port
    if args.debug is not None:
        config.debug = args.debug


def init_config(
    config_file: str = "config.yaml", args: Optional[Namespace] = None
) -> Config:
    """
    Initialize the configuration, reading from a file and environment variables.
    """

    config_file = os.path.abspath(os.path.expanduser(config_file))
    _init_config_from_file(config_file)
    _init_config_from_env()
    _init_config_from_cli(args)

    # Normalize/expand paths
    config.content_dir = os.path.abspath(os.path.expanduser(config.content_dir))

    return config


# vim:sw=4:ts=4:et:
