# retoor <retoor@molodetz.nl>

from typing import Optional
from urllib.parse import urlsplit
from fastapi import Request, HTTPException, status
from devplacepy.utils.auth import get_current_user, _request_has_auth


def safe_next(value, default="/feed"):
    if (
        value
        and value.startswith("/")
        and not value.startswith(("//", "/\\"))
        and "\\" not in value
        and "\n" not in value
        and "\r" not in value
    ):
        return value
    return default


def redirect_back(request: Request, default: str = "/feed") -> str:
    referer = request.headers.get("referer") or ""
    parts = urlsplit(referer)
    if parts.scheme and parts.netloc:
        if parts.netloc == urlsplit(str(request.base_url)).netloc:
            referer = parts.path + (f"?{parts.query}" if parts.query else "")
        else:
            referer = ""
    return safe_next(referer, default)


def cookie_secure(request: Request) -> bool:
    forwarded = request.headers.get("x-forwarded-proto", "")
    if forwarded:
        return forwarded.split(",")[0].strip().lower() == "https"
    return request.url.scheme == "https"


def client_ip(request, default: Optional[str] = "unknown") -> Optional[str]:
    headers = getattr(request, "headers", None)
    if headers is not None:
        real = headers.get("x-real-ip")
        if real and real.strip():
            return real.strip()
        forwarded = headers.get("x-forwarded-for")
        if forwarded:
            first = forwarded.split(",")[0].strip()
            if first:
                return first
    client = getattr(request, "client", None)
    if client is not None:
        return client.host
    return default


SUSPENSION_GATED_METHODS = ("POST", "PUT", "DELETE", "PATCH")

SUSPENSION_EXEMPT_PREFIXES = ("/auth", "/reports", "/block", "/mute")


def _suspension_exempt(path: str) -> bool:
    if path.startswith(SUSPENSION_EXEMPT_PREFIXES):
        return True
    return path.startswith("/profile/") and (
        path.endswith("/delete") or path.endswith("/consent")
    )


def refuse_suspended(request: Request, user: dict) -> None:
    from devplacepy.database import suspension_active

    if request.method not in SUSPENSION_GATED_METHODS:
        return
    if _suspension_exempt(request.url.path):
        return
    if not suspension_active(user):
        return
    from devplacepy.services.audit import record as audit

    reason = (user.get("suspension_reason") or "").strip()
    message = "Your account is suspended and cannot create or change content."
    if reason:
        message = f"{message} Reason: {reason}."
    audit.record(
        request,
        "security.authz.denied",
        user=user,
        result="denied",
        summary=f"suspended user denied {request.method} {request.url.path}",
    )
    raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=message)


def require_user(request: Request):
    user = get_current_user(request)
    if not user:
        from devplacepy.responses import wants_json
        from devplacepy.services.audit import record as audit

        audit.record(
            request,
            "security.authz.denied",
            user=None,
            result="denied",
            summary=f"guest denied access to {request.url.path}",
        )
        if _request_has_auth(request) or wants_json(request):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials"
            )
        raise HTTPException(
            status_code=status.HTTP_303_SEE_OTHER, headers={"Location": "/auth/login"}
        )
    refuse_suspended(request, user)
    return user


def is_admin(user: dict | None) -> bool:
    return bool(user) and user.get("role") == "Admin"


def is_primary_admin(user: dict | None) -> bool:
    from devplacepy.database import get_primary_admin_uid

    return is_admin(user) and user.get("uid") == get_primary_admin_uid()


def require_admin(request: Request):
    user = require_user(request)
    if not is_admin(user):
        from devplacepy.responses import wants_json
        from devplacepy.services.audit import record as audit

        audit.record(
            request,
            "security.authz.denied",
            user=user,
            result="denied",
            summary=f"non-admin denied access to {request.url.path}",
        )
        if wants_json(request):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Administrator access required",
            )
        raise HTTPException(
            status_code=status.HTTP_303_SEE_OTHER, headers={"Location": "/feed"}
        )
    return user


def not_found(detail: str = "Not found") -> HTTPException:
    return HTTPException(status_code=404, detail=detail)


def require_user_api(request: Request):
    user = get_current_user(request)
    if not user:
        from devplacepy.services.audit import record as audit

        audit.record(
            request,
            "security.authz.denied",
            user=None,
            result="denied",
            summary=f"guest denied access to {request.url.path}",
        )
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED, detail="Authentication required"
        )
    refuse_suspended(request, user)
    return user
