import time
from decimal import Decimal
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.repositories import SettingRepository
from app.utils.helpers import as_bool

CACHE_TTL_SECONDS = 30

DEFAULT_SETTINGS: dict[str, dict[str, Any]] = {
    "support_username": {"value": "support", "title": "Support Username", "public": True},
    "support_text": {"value": "", "title": "Support Message", "public": True},
    "channel_username": {"value": "", "title": "Channel Username", "public": True},
    "currency": {"value": "IRT", "title": "Currency Code", "public": True},
    "tron_wallet_address": {"value": "", "title": "TRON Wallet Address", "public": False},
    "trx_rate_irt": {"value": "0", "title": "TRX Price (Toman)", "public": False},
    "ton_rate_irt": {"value": "0", "title": "TON Price (Toman)", "public": False},
    "tron_min_confirmations": {"value": 1, "title": "TRON Confirmations", "public": False},
    "bank_card_number": {"value": "", "title": "Bank Card Number", "public": False},
    "bank_card_owner": {"value": "", "title": "Bank Card Owner", "public": False},
    "payment_timeout_minutes": {"value": 30, "title": "Payment Timeout (minutes)", "public": True},
    "referral_percent": {"value": "5", "title": "Referral Percent", "public": True},
    "min_topup_amount": {"value": "10000", "title": "Minimum Top-up", "public": True},
    "max_topup_amount": {"value": "500000000", "title": "Maximum Top-up", "public": True},
    "gift_premium_comment_price": {"value": "10000", "title": "Premium Comment Price", "public": True},
    "boost_max_count": {"value": 1000, "title": "Maximum Boost Count", "public": True},
    "reaction_max_count": {"value": 100000, "title": "Maximum Reaction Count", "public": True},
    "auto_process_orders": {"value": True, "title": "Process Orders Automatically", "public": False},
    "maintenance": {"value": False, "title": "Maintenance Mode", "public": True},
    "welcome_image": {"value": "", "title": "Welcome Image", "public": True},
}


class SettingsService:
    """Runtime configuration. Values are cached briefly to avoid a query per read."""

    _cache: dict[str, Any] = {}
    _cache_at: float = 0.0

    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.repo = SettingRepository(session)

    async def all(self, refresh: bool = False) -> dict[str, Any]:
        now = time.monotonic()
        if refresh or not SettingsService._cache or now - SettingsService._cache_at > CACHE_TTL_SECONDS:
            rows = await self.repo.all_settings()
            values = {key: item["value"] for key, item in DEFAULT_SETTINGS.items()}
            values.update({row.key: row.value for row in rows})
            SettingsService._cache = values
            SettingsService._cache_at = now
        return dict(SettingsService._cache)

    async def public(self) -> dict[str, Any]:
        values = await self.all()
        public_keys = {key for key, item in DEFAULT_SETTINGS.items() if item["public"]}
        rows = await self.repo.all_settings(only_public=True)
        public_keys.update(row.key for row in rows)
        return {key: value for key, value in values.items() if key in public_keys}

    async def get(self, key: str, default: Any = None) -> Any:
        values = await self.all()
        return values.get(key, default)

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

    async def get_decimal(self, key: str, default: str = "0") -> Decimal:
        value = await self.get(key, default)
        try:
            return Decimal(str(value))
        except (ValueError, ArithmeticError):
            return Decimal(default)

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

    async def get_bool(self, key: str, default: bool = False) -> bool:
        return as_bool(await self.get(key, default), default)

    async def set(self, key: str, value: Any, title: str | None = None) -> None:
        default = DEFAULT_SETTINGS.get(key, {})
        await self.repo.upsert(key, value, title or default.get("title"))
        self.invalidate()

    async def set_many(self, values: dict[str, Any]) -> None:
        for key, value in values.items():
            default = DEFAULT_SETTINGS.get(key, {})
            await self.repo.upsert(key, value, default.get("title"))
        self.invalidate()

    async def ensure_defaults(self) -> None:
        existing = {row.key for row in await self.repo.all_settings()}
        for key, item in DEFAULT_SETTINGS.items():
            if key not in existing:
                await self.repo.create(
                    key=key, value=item["value"], title=item["title"], is_public=item["public"]
                )
        self.invalidate()

    @staticmethod
    def invalidate() -> None:
        SettingsService._cache = {}
        SettingsService._cache_at = 0.0
