"""Encrypted, admin-portal-editable platform settings (V2 §0.8 direction).

Lets ops rotate sensitive provider credentials (AzamPay client secret, etc.)
from the admin UI, backed by PlatformSetting, instead of requiring .env edits
+ a redeploy. `get_setting()` checks the DB first and falls back to an env
var so local dev keeps working with a plain .env and no admin account.

Encryption key: SETTINGS_ENCRYPTION_KEY (a random string) should be set in
production. If absent, DJANGO_SECRET_KEY is used to derive one — convenient
for local dev, but a dedicated key (rotatable independently of SECRET_KEY)
should be set before this holds anything sensitive in production.
"""

import base64
import hashlib

from cryptography.fernet import Fernet, InvalidToken
from django.conf import settings


def _cipher() -> Fernet:
	key_material = getattr(settings, "SETTINGS_ENCRYPTION_KEY", "") or settings.SECRET_KEY
	derived_key = base64.urlsafe_b64encode(hashlib.sha256(key_material.encode()).digest())
	return Fernet(derived_key)


def get_setting(key: str, settings_attr: str = "", default: str = "") -> str:
	"""DB value (decrypted) if set, else getattr(django.conf.settings, settings_attr).

	Resolves through Django's settings object rather than raw os.environ so
	this respects override_settings() in tests and any env-var handling
	settings.py already does (defaults, stripping, etc.) — settings.py is the
	single source of truth for "env var or default", not a second copy here.
	"""
	from .models import PlatformSetting

	row = PlatformSetting.objects.filter(key=key).first()
	if row is not None:
		try:
			return _cipher().decrypt(row.encrypted_value.encode()).decode()
		except InvalidToken:
			pass  # corrupted/foreign-key row — fall through to settings
	if settings_attr:
		return getattr(settings, settings_attr, default)
	return default


def set_setting(key: str, value: str, *, updated_by: str = "") -> None:
	from .models import PlatformSetting

	token = _cipher().encrypt(value.encode()).decode()
	PlatformSetting.objects.update_or_create(
		key=key, defaults={"encrypted_value": token, "updated_by": updated_by[:120]}
	)


def clear_setting(key: str) -> None:
	from .models import PlatformSetting

	PlatformSetting.objects.filter(key=key).delete()


def has_db_value(key: str) -> bool:
	from .models import PlatformSetting

	return PlatformSetting.objects.filter(key=key).exists()


def mask(value: str) -> str:
	"""Never echo a secret back in full — show only its last 4 characters."""
	if not value:
		return ""
	tail = value[-4:] if len(value) > 4 else value
	return f"{'•' * max(len(value) - len(tail), 4)}{tail}"
