from __future__ import annotations

from datetime import timedelta
from typing import Any

from django.db.models import Q
from django.utils import timezone
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from apps.users.models import Business, User
from config.reporting_utils import _admin_role, _legacy_profile_for_user


def _require_master_admin(request) -> tuple[dict[str, Any] | None, Response | None]:
    """
    Master endpoints must be accessible to users authenticated via `master_login`.

    In local/dev and some deployments, a master admin user may not have a legacy
    (business-scoped) profile row; requiring one incorrectly blocks the master UI
    with 403 "No business profile for user."
    """
    u = getattr(request, "user", None)
    if u is not None and (getattr(u, "is_staff", False) or getattr(u, "is_superuser", False)):
        return None, None

    legacy = _legacy_profile_for_user(u)
    if legacy and _admin_role(legacy.get("role")):
        return legacy, None

    # Keep response shape stable for UI error handling.
    return legacy, Response({"success": False, "message": "Admin access required."}, status=403)


def _int_param(val: Any, default: int) -> int:
    try:
        return int(val)
    except Exception:
        return default


def _business_to_master_dict(b: Business) -> dict[str, Any]:
    # Match masterpanel needs; return non-DB fields as null.
    return {
        "business_id": b.business_id,
        "created_at": b.created_at.isoformat() if b.created_at else None,
        "updated_at": b.updated_at.isoformat() if b.updated_at else None,
        "business_name": b.business_name,
        "avail_min": b.avail_min,
        "remain_min": b.remain_min,
        "contact_name": b.contact_name,
        "gst_no": b.gst_no,
        "contact_number": b.contact_number,
        "contact_email": b.contact_email,
        "username": b.username,
        "password": "",
        "pan_no": b.pan_no,
        "business_address": b.business_address,
        "status": b.status,
        "plans": b.plans,
        "retry_attempt": b.retry_attempt,
        "business_time": b.business_time,
        "last_login": b.last_login.isoformat() if b.last_login else None,
        "remember_token": b.remember_token,
        "mcube_bid": b.mcube_bid,
        "mcube_agent_id": b.mcube_agent_id,
        "auth_token": b.auth_token,
        "stt_provider": b.stt_provider,
        "tts_provider": b.tts_provider,
        "stt_model": b.stt_model,
        "tts_model_id": b.tts_model_id,
        "ch_language": b.ch_language,
        # Non-DB field expected by UI
        "domain": None,
    }


def _apply_business_payload(b: Business, payload: dict[str, Any], *, allow_password_update: bool) -> None:
    # Map masterpanel payload -> DB columns. Unknown keys are ignored.
    allowed = {
        "business_name",
        "avail_min",
        "remain_min",
        "contact_name",
        "gst_no",
        "contact_number",
        "contact_email",
        "username",
        "password",
        "pan_no",
        "business_address",
        "status",
        "plans",
        "retry_attempt",
        "business_time",
        "mcube_bid",
        "mcube_agent_id",
        "auth_token",
        "stt_provider",
        "tts_provider",
        "stt_model",
        "tts_model_id",
        "ch_language",
        # Backward-compat if any clients still send it
        "camp_auth_token",
    }

    for key, val in (payload or {}).items():
        if key not in allowed:
            continue

        if key in {"camp_auth_token"}:
            b.auth_token = "" if val is None else str(val)
            continue

        if key == "password":
            if not allow_password_update:
                continue
            if val is None:
                continue
            plain = str(val).strip()
            if not plain:
                continue
            import bcrypt as _bcrypt
            hashed = _bcrypt.hashpw(plain.encode("utf-8"), _bcrypt.gensalt(rounds=10))
            # Store with $2y$ prefix so Laravel-style login verification works
            b.password = hashed.decode("utf-8").replace("$2b$", "$2y$", 1)
            continue

        if key == "avail_min":
            if val in ("", None):
                b.avail_min = None
            else:
                b.avail_min = _int_param(val, 0)
            continue

        if key == "plans":
            # plans is an ENUM column — skip empty string to avoid MySQL truncation error
            if val in ("", None):
                continue
            b.plans = str(val)
            continue

        if hasattr(b, key):
            setattr(b, key, val)


def _create_business_admin_user(b: Business) -> None:
    """Insert an admin user row for the newly created business."""
    try:
        User.objects.create(
            username=b.username or b.contact_email or f"admin_{b.business_id}",
            name=b.contact_name or b.business_name,
            email=b.contact_email or "",
            password=b.password or "",
            role="admin",
            status="active",
            business=b,
        )
    except Exception:
        pass


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def master_businesses_collection(request):
    _, err = _require_master_admin(request)
    if err:
        return err

    if request.method == "GET":
        q = request.query_params
        search = (q.get("search") or "").strip()
        status = (q.get("status") or "").strip()
        sort_by = (q.get("sort_by") or "created_at").strip()
        sort_order = (q.get("sort_order") or "desc").strip().lower()
        per_page = max(1, min(200, _int_param(q.get("per_page") or 10, 10)))
        page = max(1, _int_param(q.get("page") or 1, 1))

        qs = Business.objects.all()
        if search:
            qs = qs.filter(
                Q(business_name__icontains=search)
                | Q(contact_name__icontains=search)
                | Q(contact_email__icontains=search)
                | Q(contact_number__icontains=search)
                | Q(username__icontains=search)
            )
        if status:
            qs = qs.filter(status=status)

        sortable = {"created_at", "updated_at", "business_name", "status", "business_id"}
        if sort_by not in sortable:
            sort_by = "created_at"
        order_prefix = "-" if sort_order == "desc" else ""
        qs = qs.order_by(f"{order_prefix}{sort_by}")

        total = qs.count()
        start = (page - 1) * per_page
        end = start + per_page
        rows = list(qs[start:end])
        data = [_business_to_master_dict(b) for b in rows]

        return Response(
            {
                "success": True,
                "data": data,
                "pagination": {
                    "page": page,
                    "per_page": per_page,
                    "total": total,
                    "total_pages": (total + per_page - 1) // per_page,
                },
            }
        )

    payload = request.data if isinstance(request.data, dict) else {}
    name = str(payload.get("business_name") or "").strip()
    if not name:
        return Response({"success": False, "message": "business_name is required."}, status=400)

    b = Business(business_name=name)
    _apply_business_payload(b, payload, allow_password_update=True)
    b.save()
    _create_business_admin_user(b)
    return Response({"success": True, "data": _business_to_master_dict(b)}, status=201)


@api_view(["GET", "PUT", "PATCH", "DELETE"])
@permission_classes([IsAuthenticated])
def master_business_detail(request, business_id: int):
    _, err = _require_master_admin(request)
    if err:
        return err

    b = Business.objects.filter(pk=int(business_id)).first()
    if not b:
        return Response({"success": False, "message": "Business not found."}, status=404)

    if request.method == "GET":
        return Response({"success": True, "data": _business_to_master_dict(b)})

    if request.method == "DELETE":
        b.delete()
        return Response({"success": True, "message": "Business deleted."})

    payload = request.data if isinstance(request.data, dict) else {}
    _apply_business_payload(b, payload, allow_password_update=True)
    b.save()
    return Response({"success": True, "data": _business_to_master_dict(b), "message": "Business updated."})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def master_businesses_statistics(request):
    _, err = _require_master_admin(request)
    if err:
        return err

    now = timezone.now()
    recent_since = now - timedelta(days=7)

    total = Business.objects.count()
    active = Business.objects.filter(status="active").count()
    pending = Business.objects.filter(status="pending").count()
    inactive = Business.objects.filter(status="inactive").count()
    recent = Business.objects.filter(created_at__gte=recent_since).count()

    return Response(
        {
            "success": True,
            "data": {
                "total_businesses": total,
                "active_businesses": active,
                "pending_businesses": pending,
                "inactive_businesses": inactive,
                "recent_businesses": recent,
            },
        }
    )


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def master_business_check_username(request):
    _, err = _require_master_admin(request)
    if err:
        return err

    username = (request.query_params.get("username") or "").strip()
    exclude_id = request.query_params.get("exclude_id")
    qs = Business.objects.all()
    if exclude_id not in (None, ""):
        try:
            qs = qs.exclude(pk=int(exclude_id))
        except Exception:
            pass
    exists = bool(username) and qs.filter(username=username).exists()
    return Response({"success": True, "available": (not exists)})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def master_business_check_email(request):
    _, err = _require_master_admin(request)
    if err:
        return err

    email = (request.query_params.get("email") or "").strip()
    exclude_id = request.query_params.get("exclude_id")
    qs = Business.objects.all()
    if exclude_id not in (None, ""):
        try:
            qs = qs.exclude(pk=int(exclude_id))
        except Exception:
            pass
    exists = bool(email) and qs.filter(contact_email=email).exists()
    return Response({"success": True, "available": (not exists)})


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def master_business_check_domain(request):
    _, err = _require_master_admin(request)
    if err:
        return err

    # `domain` column is not present in DB; treat any domain as available.
    return Response({"success": True, "available": True})

