"""Validated application settings stored in the database with code defaults."""

from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import UTC, datetime
from typing import Any
from zoneinfo import ZoneInfo

from app.database.base import utcnow
from app.database.models import Post
from app.database.repositories import Repositories
from app.services.templates import (
    DEFAULT_CAPTION_TEMPLATE,
    parse_buttons,
    render_caption,
    resolve_cover,
)

DEFAULTS: dict[str, str] = {
    "caption_template": DEFAULT_CAPTION_TEMPLATE,
    "caption_prefix": "",
    "caption_suffix": "",
    "brand_signature": "",
    "default_cover_file_id": "",
    "use_media_thumbnail_as_cover": "true",
    "video_link_template": "https://t.me/{bot_username}?start={token}",
    "buttons": "[]",
    "membership_enabled": "true",
    "membership_message": (
        "برای دریافت ویدیو، ابتدا در کانال‌های زیر عضو شوید.\n"
        "سپس دکمه «بررسی عضویت» را بزنید."
    ),
    "membership_check_button": "✅ بررسی عضویت",
    "auto_delete_enabled": "false",
    "user_delete_seconds": "0",
    "channel_delete_seconds": "0",
    "protect_content": "false",
    "auto_ingest_enabled": "true",
    "publish_enabled": "true",
    "rate_limit_requests": "5",
    "rate_limit_window_seconds": "60",
    "rate_limit_min_interval_seconds": "2",
    "bot_username": "",
    "timezone": "Asia/Tehran",
    "max_caption_length": "1024",
}


@dataclass(frozen=True, slots=True)
class DeletePolicy:
    enabled: bool
    user_seconds: int
    channel_seconds: int


class SettingsService:
    def __init__(
        self,
        repos: Repositories,
        *,
        timezone: str = "Asia/Tehran",
        max_caption_length: int = 1024,
    ) -> None:
        self.repos = repos
        self.default_timezone = timezone
        self.default_max_caption_length = max_caption_length

    async def ensure_defaults(self) -> None:
        existing = await self.repos.settings.all()
        for key, value in DEFAULTS.items():
            if key == "timezone":
                value = self.default_timezone
            elif key == "max_caption_length":
                value = str(self.default_max_caption_length)
            if key not in existing:
                await self.repos.settings.set(key, value)

    async def get(self, key: str) -> str:
        value = await self.repos.settings.get(key)
        return DEFAULTS.get(key, "") if value is None else value

    async def get_int(self, key: str, default: int = 0) -> int:
        try:
            return int(await self.get(key))
        except (TypeError, ValueError):
            return default

    async def get_bool(self, key: str, default: bool = False) -> bool:
        value = (await self.get(key)).strip().lower()
        if value in {"1", "true", "yes", "on"}:
            return True
        if value in {"0", "false", "no", "off"}:
            return False
        return default

    async def get_json(self, key: str, default: Any) -> Any:
        try:
            return json.loads(await self.get(key))
        except (TypeError, ValueError):
            return default

    async def set(self, key: str, value: str | int | bool, actor_id: int | None = None) -> None:
        if isinstance(value, bool):
            text = "true" if value else "false"
        else:
            text = str(value)
        await self.repos.settings.set(key, text, actor_id)

    async def buttons(self, post: Post | None = None) -> list[tuple[str, str]]:
        if post is not None and post.buttons_override is not None:
            return parse_buttons(post.buttons_override)
        return parse_buttons(await self.get_json("buttons", []))

    async def build_caption(
        self,
        post: Post,
        *,
        channel_name: str | None = None,
        channel_username: str | None = None,
        publish_date: datetime | None = None,
    ) -> str:
        template = post.caption_template_override or await self.get("caption_template")
        if post.caption_override:
            template = post.caption_override
        description = post.description
        if not description and post.media_asset is not None:
            description = post.media_asset.original_caption
        global_brand = await self.get("brand_signature")
        brand = (
            post.signature_override
            if post.signature_override is not None
            else global_brand
        )
        prefix = (
            post.caption_prefix_override
            if post.caption_prefix_override is not None
            else await self.get("caption_prefix")
        )
        suffix = (
            post.caption_suffix_override
            if post.caption_suffix_override is not None
            else await self.get("caption_suffix")
        )
        effective_date = publish_date or post.published_at or utcnow()
        if effective_date.tzinfo is None:
            effective_date = effective_date.replace(tzinfo=UTC)
        # The caption variable is a human-facing value: render it in the
        # configurable display timezone, while all persistence stays UTC.
        effective_date = effective_date.astimezone(ZoneInfo(await self.timezone()))
        return render_caption(
            template=template,
            title=post.title,
            description=description,
            video_link=post.video_link,
            post_id=post.id,
            publish_date=effective_date,
            channel_name=channel_name,
            channel_username=channel_username,
            brand=brand,
            media_file_name=post.media_asset.file_name if post.media_asset else None,
            prefix=prefix,
            suffix=suffix,
            signature=brand,
            max_length=await self.get_int("max_caption_length", 1024),
        )

    async def cover(self, post: Post) -> str | None:
        return resolve_cover(
            post,
            global_cover=await self.get("default_cover_file_id") or None,
            use_media_thumbnail=await self.get_bool("use_media_thumbnail_as_cover", True),
        )

    async def delete_policy(self, post: Post) -> DeletePolicy:
        global_enabled = await self.get_bool("auto_delete_enabled", False)
        enabled = global_enabled if post.auto_delete_enabled is None else post.auto_delete_enabled
        user_seconds = (
            post.user_delete_after_seconds
            if post.user_delete_after_seconds is not None
            else await self.get_int("user_delete_seconds", 0)
        )
        channel_seconds = (
            post.channel_delete_after_seconds
            if post.channel_delete_after_seconds is not None
            else await self.get_int("channel_delete_seconds", 0)
        )
        return DeletePolicy(
            enabled=bool(enabled),
            user_seconds=max(0, user_seconds) if enabled else 0,
            channel_seconds=max(0, channel_seconds) if enabled else 0,
        )

    async def protect_content(self, post: Post) -> bool:
        if post.protect_content is not None:
            return post.protect_content
        return await self.get_bool("protect_content", False)

    async def bot_username(self) -> str | None:
        value = (await self.get("bot_username")).strip().lstrip("@")
        return value or None

    async def timezone(self) -> str:
        return await self.get("timezone") or self.default_timezone
