import os
import warnings
from datetime import timedelta
from urllib.parse import urlparse

from dotenv import load_dotenv

_BASE_DIR_FOR_ENV = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(_BASE_DIR_FOR_ENV, ".env"))

_DEV_SECRET_KEY = "dev-only-insecure-key-do-not-use-in-production"


def _env_bool(name, default=False):
    raw = os.environ.get(name)
    if raw is None:
        return default
    return raw.strip().lower() in {"1", "true", "yes", "on"}


def _env_str(name, default=None):
    raw = os.environ.get(name)
    if raw is None:
        return default
    value = raw.strip()
    return value if value else default


def _env_int(name, default=0):
    raw = _env_str(name)
    if raw is None:
        return int(default)
    try:
        return int(raw)
    except (TypeError, ValueError):
        return int(default)


def database_summary(database_url: str | None) -> str:
    uri = (database_url or "").strip()
    if not uri:
        return "no configurada (DATABASE_URL ausente)"

    if uri.startswith("sqlite"):
        if uri.startswith("sqlite:///"):
            path = uri[len("sqlite:///") :]
        else:
            path = uri.split("://", 1)[-1]
        return f"SQLite ({path})"

    if uri.startswith(("postgresql://", "postgresql+")):
        parsed = urlparse(uri)
        host = parsed.hostname or "localhost"
        port = parsed.port or 5432
        name = (parsed.path or "").lstrip("/") or "?"
        user = parsed.username or "?"
        return f"PostgreSQL ({user}@{host}:{port}/{name})"

    scheme = uri.split("://", 1)[0] if "://" in uri else "desconocido"
    return f"{scheme} (URL personalizada)"


class Config:
    BASE_DIR = os.path.abspath(os.path.dirname(__file__))
    FLASK_ENV = (_env_str("FLASK_ENV", "development") or "development").lower()

    DEBUG = _env_bool("FLASK_DEBUG", default=False)

    _secret_from_env = _env_str("SECRET_KEY")
    if _secret_from_env:
        SECRET_KEY = _secret_from_env
    elif DEBUG:
        SECRET_KEY = _DEV_SECRET_KEY
    else:
        SECRET_KEY = None

    PUBLIC_BASE_URL = (_env_str("PUBLIC_BASE_URL", "https://playnow.a.pinggy.link") or "").rstrip("/")
    SQLALCHEMY_DATABASE_URI = _env_str("DATABASE_URL")
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    WTF_CSRF_TIME_LIMIT = None

    SESSION_COOKIE_HTTPONLY = True
    SESSION_COOKIE_SAMESITE = _env_str("SESSION_COOKIE_SAMESITE", "Lax") or "Lax"
    SESSION_COOKIE_SECURE = _env_bool("SESSION_COOKIE_SECURE") or _env_bool("USE_HTTPS")
    PERMANENT_SESSION_LIFETIME = timedelta(days=int(_env_str("SESSION_LIFETIME_DAYS", "30") or "30"))
    ADMIN_SESSION_LIFETIME_DAYS = int(_env_str("ADMIN_SESSION_LIFETIME_DAYS", "7") or "7")

    ADMIN_USERNAME = _env_str("ADMIN_USERNAME", "admin") or "admin"
    ADMIN_PASSWORD = _env_str("ADMIN_PASSWORD")
    ADMIN_PASSWORD_HASH = _env_str("ADMIN_PASSWORD_HASH")
    ADMIN_COOKIE_NAME = _env_str("ADMIN_COOKIE_NAME", "admin_auth") or "admin_auth"
    RATELIMIT_STORAGE_URI = _env_str("RATELIMIT_STORAGE_URI", "memory://") or "memory://"
    ADMIN_ALLOWED_IPS = _env_str("ADMIN_ALLOWED_IPS", "") or ""
    ADMIN_SUBDOMAIN_ONLY = _env_bool("ADMIN_SUBDOMAIN_ONLY", default=False)
    ADMIN_SUBDOMAIN_NAME = _env_str("ADMIN_SUBDOMAIN_NAME", "admin") or "admin"
    SECURITY_ALERT_WEBHOOK_URL = _env_str("SECURITY_ALERT_WEBHOOK_URL")
    ADMIN_MFA_FORCE_ALL = _env_bool("ADMIN_MFA_FORCE_ALL", default=False)

    PROXY_FIX_X_FOR = _env_int("PROXY_FIX_X_FOR", 1 if FLASK_ENV == "production" else 0)
    PROXY_FIX_X_PROTO = _env_int("PROXY_FIX_X_PROTO", 1 if FLASK_ENV == "production" else 0)
    PROXY_FIX_X_HOST = _env_int("PROXY_FIX_X_HOST", 1 if FLASK_ENV == "production" else 0)
    PROXY_FIX_X_PORT = _env_int("PROXY_FIX_X_PORT", 1 if FLASK_ENV == "production" else 0)
    PROXY_FIX_X_PREFIX = _env_int("PROXY_FIX_X_PREFIX", 0)

    UPLOAD_FOLDER = os.path.join(BASE_DIR, "static", "uploads")
    MAX_CONTENT_LENGTH = 10 * 1024 * 1024

    PREFERRED_URL_SCHEME = "https" if SESSION_COOKIE_SECURE else "http"

    @classmethod
    def database_backend(cls) -> str:
        uri = cls.SQLALCHEMY_DATABASE_URI or ""
        if uri.startswith(("postgresql://", "postgresql+")):
            return "postgresql"
        if uri.startswith("sqlite"):
            return "sqlite"
        return "unknown"

    @classmethod
    def validate(cls):
        errors = []
        is_production = cls.FLASK_ENV == "production"
        if not cls.SQLALCHEMY_DATABASE_URI:
            errors.append(
                "DATABASE_URL es obligatorio. Define sqlite:///ruta/a/db.sqlite o postgresql+psycopg2://usuario:password@host:5432/base en .env."
            )
        elif not cls.SQLALCHEMY_DATABASE_URI.startswith(("postgresql://", "postgresql+")):
            if not cls.SQLALCHEMY_DATABASE_URI.startswith("sqlite"):
                errors.append("DATABASE_URL debe usar el esquema sqlite:/// o postgresql:// / postgresql+psycopg2://.")
        if not cls.SECRET_KEY:
            errors.append("SECRET_KEY es obligatorio cuando FLASK_DEBUG=0.")
        elif cls.SECRET_KEY == _DEV_SECRET_KEY and not cls.DEBUG:
            errors.append("SECRET_KEY de desarrollo detectada con FLASK_DEBUG=0.")
        if cls.DEBUG and is_production:
            warnings.warn("FLASK_DEBUG=1 con FLASK_ENV=production no es recomendable.", stacklevel=2)
        if is_production:
            if not any(
                (
                    cls.PROXY_FIX_X_FOR,
                    cls.PROXY_FIX_X_PROTO,
                    cls.PROXY_FIX_X_HOST,
                    cls.PROXY_FIX_X_PORT,
                    cls.PROXY_FIX_X_PREFIX,
                )
            ):
                warnings.warn(
                    "ProxyFix esta desactivado en produccion. Si la app esta detras de Cloudflare, Nginx o cPanel, configura PROXY_FIX_X_FOR/PROTO/HOST/PORT.",
                    stacklevel=2,
                )
            if cls.SQLALCHEMY_DATABASE_URI.startswith("sqlite"):
                warnings.warn(
                    "Produccion ejecutandose con SQLite. Asegurate de que el archivo de base de datos persista y tenga permisos de escritura.",
                    stacklevel=2,
                )
            if not cls.ADMIN_PASSWORD_HASH:
                errors.append("ADMIN_PASSWORD_HASH es obligatorio en produccion.")
            if cls.ADMIN_PASSWORD:
                errors.append("ADMIN_PASSWORD en texto plano no se permite en produccion.")
            if cls.RATELIMIT_STORAGE_URI and cls.RATELIMIT_STORAGE_URI.startswith("memory://"):
                warnings.warn(
                    "RATELIMIT_STORAGE_URI=memory:// en produccion solo protege por proceso. Para multi-proceso usa Redis si mas adelante lo necesitas.",
                    stacklevel=2,
                )
            if not cls.PUBLIC_BASE_URL:
                errors.append("PUBLIC_BASE_URL es obligatorio en produccion.")
            else:
                parsed = urlparse(cls.PUBLIC_BASE_URL)
                host = (parsed.hostname or "").lower()
                if parsed.scheme != "https":
                    errors.append("PUBLIC_BASE_URL debe usar https:// en produccion.")
                if any(
                    marker in host
                    for marker in ("localhost", "127.0.0.1", "::1", "pinggy", "ngrok")
                ):
                    errors.append("PUBLIC_BASE_URL no puede apuntar a localhost, Pinggy o Ngrok en produccion.")
            if not cls.SECRET_KEY or cls.SECRET_KEY == _DEV_SECRET_KEY or len(cls.SECRET_KEY) < 32:
                errors.append("SECRET_KEY debe ser fuerte (>=32 caracteres) en produccion.")
        elif cls.ADMIN_PASSWORD and len(cls.ADMIN_PASSWORD) < 12:
            warnings.warn(
                "ADMIN_PASSWORD es corta; usa al menos 12 caracteres en desarrollo.",
                stacklevel=2,
            )
            if cls.SESSION_COOKIE_SECURE and not cls.PUBLIC_BASE_URL.startswith("https://"):
                warnings.warn(
                "SESSION_COOKIE_SECURE=True pero PUBLIC_BASE_URL no usa https://.",
                stacklevel=2,
            )
        if cls.ADMIN_SUBDOMAIN_ONLY and not cls.ADMIN_SUBDOMAIN_NAME:
            errors.append("ADMIN_SUBDOMAIN_NAME no puede estar vacio si ADMIN_SUBDOMAIN_ONLY=1.")
        if errors:
            raise RuntimeError("Configuracion insegura:\n- " + "\n- ".join(errors))
