from urllib.parse import urlencode, urlsplit, urlunsplit

from django.urls import NoReverseMatch, reverse
from django.utils.http import url_has_allowed_host_and_scheme

from contract.access import can_access_contract_dashboard
from contract.models import User


BUYER_SAFE_EXACT_PATHS = {
    "/",
    "/about/",
    "/contracts/",
    "/dashboard/buyer/",
    "/contracts/buyer_dashboard/",
    "/market/dashboard/buyer/",
}

BUYER_SAFE_PREFIXES = (
    "/catalog/",
    "/products/",
    "/listing/",
    "/storefronts/",
    "/cart/",
    "/checkout/",
    "/orders/",
    "/sellers/apply/",
    "/sellers/status/",
)

LOGIN_LOOP_PATHS = {
    "/login/",
    "/logout/",
    "/contracts/login/",
    "/contracts/logout/",
    "/contracts/marketplace/login/",
}

API_ONLY_PREFIXES = (
    "/contracts/api/",
    "/market/api/",
    "/payments/api/",
    "/payments/callback/",
    "/payments/check-",
    "/payments/transaction-status/",
)

BUYER_DASHBOARD_BLOCKED_PREFIXES = (
    "/dashboard/",
    "/contracts/dashboard/",
    "/market/dashboard/",
    "/contracts/contracts/",
    "/contracts/contract-requests/",
    "/contracts/contract-terms/",
    "/contracts/loans/",
    "/contracts/payments/",
    "/contracts/marketplace-products/",
    "/contracts/posting-fees/",
    "/contracts/sales/",
)


def _reverse_or_fallback(route_name, fallback="/"):
    try:
        return reverse(route_name)
    except NoReverseMatch:
        return fallback


def _default_home():
    return _reverse_or_fallback("market:home", "/")


def _default_buyer_destination():
    return _reverse_or_fallback("market:buyer_orders", _default_home())


def _normalize_next_url(next_url):
    if not next_url:
        return ""
    next_url = str(next_url).strip()
    if not next_url:
        return ""
    parts = urlsplit(next_url)
    normalized_path = parts.path or "/"
    normalized_query = parts.query or ""
    normalized_fragment = parts.fragment or ""
    return urlunsplit(("", "", normalized_path, normalized_query, normalized_fragment))


def _next_path(next_url):
    normalized = _normalize_next_url(next_url)
    if not normalized:
        return ""
    return urlsplit(normalized).path or "/"


def _is_buyer(user):
    return getattr(user, "type", "") == User.Types.BUYER


def _is_internal_dashboard_user(user):
    return can_access_contract_dashboard(user)


def _path_has_allowed_prefix(path, prefixes):
    return any(path.startswith(prefix) for prefix in prefixes)


def _is_web_redirect_excluded_path(path):
    return path in LOGIN_LOOP_PATHS or _path_has_allowed_prefix(path, API_ONLY_PREFIXES)


def _map_buyer_compatibility_path(path):
    if path in BUYER_SAFE_EXACT_PATHS:
        return _default_buyer_destination() if path != "/" else _default_home()
    return None


def is_safe_next_url(request, next_url):
    if not next_url:
        return False
    allowed_hosts = set()
    if request is not None:
        try:
            allowed_hosts.add(request.get_host())
        except Exception:
            pass
    require_https = bool(getattr(request, "is_secure", lambda: False)()) if request is not None else False
    return url_has_allowed_host_and_scheme(
        url=str(next_url).strip(),
        allowed_hosts=allowed_hosts,
        require_https=require_https,
    )


def is_marketplace_public_or_buyer_path(path):
    normalized_path = _next_path(path)
    if not normalized_path:
        return False
    if normalized_path in BUYER_SAFE_EXACT_PATHS:
        return True
    return _path_has_allowed_prefix(normalized_path, BUYER_SAFE_PREFIXES)


def get_web_dashboard_destination(user):
    if not getattr(user, "is_authenticated", False) or not getattr(user, "is_active", True):
        return _default_home()
    if _is_internal_dashboard_user(user):
        return _reverse_or_fallback("unified_dashboard", _default_home())
    if _is_buyer(user):
        return _default_buyer_destination()
    return _default_home()


def get_web_post_login_redirect(user, request=None, next_url=None):
    if not getattr(user, "is_authenticated", False) or not getattr(user, "is_active", True):
        return _default_home()

    if next_url and is_safe_next_url(request, next_url):
        normalized_next = _normalize_next_url(next_url)
        next_path = _next_path(normalized_next)

        if next_path and not _is_web_redirect_excluded_path(next_path):
            if _is_buyer(user):
                buyer_alias_target = _map_buyer_compatibility_path(next_path)
                if buyer_alias_target:
                    return buyer_alias_target
                if _path_has_allowed_prefix(next_path, BUYER_DASHBOARD_BLOCKED_PREFIXES):
                    return _default_buyer_destination()
                if is_marketplace_public_or_buyer_path(next_path):
                    return normalized_next
            else:
                return normalized_next

    return get_web_dashboard_destination(user)


def get_web_logout_redirect():
    return _default_home()


def build_web_login_url(request=None, next_url=None):
    login_url = _reverse_or_fallback("market:login", "/login/")
    candidate_next = next_url
    if not candidate_next and request is not None:
        candidate_next = request.get_full_path()
    if candidate_next and is_safe_next_url(request, candidate_next):
        separator = "&" if "?" in login_url else "?"
        return f"{login_url}{separator}{urlencode({'next': _normalize_next_url(candidate_next)})}"
    return login_url
