"""
REST endpoints for legacy `businesses.business_time` and `businesses.retry_attempt`,
used by campaign callbacks UI (business hours + retry intervals).
"""

from __future__ import annotations

import json
import re
from typing import Any

from django.shortcuts import get_object_or_404
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
from config.auth_views import _fetch_legacy_user

RETRY_KEY_RE = re.compile(r"^retry_attempt_(\d+)$")


def _identifier(request) -> str:
    u = request.user
    return (getattr(u, "email", None) or getattr(u, "username", None) or "").strip()


def _caller_business_id(request) -> int | None:
    ident = _identifier(request)
    legacy = _fetch_legacy_user(ident) if ident else None
    if not legacy:
        return None
    bid = legacy.get("business_id")
    try:
        return int(bid) if bid is not None else None
    except Exception:
        return None


def _ensure_business_access(request, business_id: int) -> tuple[bool, Response | None]:
    caller_bid = _caller_business_id(request)
    if caller_bid is None:
        return False, Response(
            {"success": False, "message": "Business not found for current user."},
            status=403,
        )
    if caller_bid != business_id:
        return False, Response(
            {"success": False, "message": "You cannot access this business."},
            status=403,
        )
    return True, None


def _retry_sort_key(k: str) -> int:
    m = RETRY_KEY_RE.match(k)
    return int(m.group(1)) if m else 0


def _parse_retry_column(raw: Any) -> tuple[dict[str, int], bool]:
    """Parse `businesses.retry_attempt` into ({retry_attempt_n: hours}, enabled)."""
    enabled = True
    if raw is None or raw == "":
        return {}, enabled

    if isinstance(raw, int):
        # Legacy single numeric column → one slot (may be 0 hours)
        return {"retry_attempt_1": int(raw)}, enabled

    s = raw.strip() if isinstance(raw, str) else str(raw).strip()
    if not s:
        return {}, enabled

    try:
        obj = json.loads(s)
    except json.JSONDecodeError:
        if s.isdigit():
            return {"retry_attempt_1": int(s)}, enabled
        return {}, enabled

    if not isinstance(obj, dict):
        return {}, enabled

    enabled = bool(obj["enabled"]) if "enabled" in obj else True
    hours: dict[str, int] = {}
    for k, v in obj.items():
        if k == "enabled":
            continue
        if RETRY_KEY_RE.match(k):
            try:
                hours[k] = int(v)
            except (TypeError, ValueError):
                pass
    return hours, enabled


def _persist_retry_column(hours: dict[str, int], enabled: bool) -> str:
    obj: dict[str, Any] = {"enabled": enabled}
    for k in sorted(hours.keys(), key=_retry_sort_key):
        obj[k] = hours[k]
    return json.dumps(obj)


def _renumber_retry_keys(hours: dict[str, int]) -> dict[str, int]:
    ordered = sorted(hours.items(), key=lambda kv: _retry_sort_key(kv[0]))
    return {f"retry_attempt_{i + 1}": val for i, (_, val) in enumerate(ordered)}


def _business_to_response_dict(b: Business) -> dict[str, Any]:
    bt_raw = b.business_time or ""
    bt_out: Any = bt_raw
    if bt_raw:
        try:
            bt_out = json.loads(bt_raw)
        except json.JSONDecodeError:
            pass

    ra_hours, ra_enabled = _parse_retry_column(b.retry_attempt)
    ra_out: dict[str, Any] = {"enabled": ra_enabled, **ra_hours}

    return {
        "business_id": b.business_id,
        "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,
        "pan_no": b.pan_no,
        "business_address": b.business_address,
        "status": b.status,
        "plans": b.plans,
        "business_time": bt_out,
        "retry_attempt": ra_out,
        "mcube_bid": b.mcube_bid,
        "mcube_agent_id": b.mcube_agent_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,
        # auth_token / password intentionally omitted
    }


@api_view(["GET", "PUT", "PATCH"])
@permission_classes([IsAuthenticated])
def business_detail_update(request, business_id: int):
    ok, err = _ensure_business_access(request, business_id)
    if not ok:
        return err  # type: ignore[misc]

    b = get_object_or_404(Business, pk=business_id)

    if request.method == "GET":
        return Response({"success": True, "data": _business_to_response_dict(b)})

    data = request.data
    if not isinstance(data, dict):
        return Response({"success": False, "message": "Invalid JSON body."}, status=400)

    allowed = {
        "business_name",
        "avail_min",
        "remain_min",
        "contact_name",
        "gst_no",
        "contact_number",
        "contact_email",
        "username",
        "password",
        "pan_no",
        "business_address",
        "status",
        "plans",
        "business_time",
        "retry_attempt",
        "mcube_bid",
        "mcube_agent_id",
        "auth_token",
    }

    for key in data:
        if key not in allowed:
            continue
        val = data[key]

        if key == "business_time":
            if val is None:
                b.business_time = ""
            elif isinstance(val, (dict, list)):
                b.business_time = json.dumps(val)
            else:
                b.business_time = str(val)

        elif key == "retry_attempt":
            if val is None:
                b.retry_attempt = "{}"
            elif isinstance(val, dict):
                hours, _en = _parse_retry_column(json.dumps(val))
                enabled_guess = bool(val["enabled"]) if "enabled" in val else _en
                b.retry_attempt = _persist_retry_column(hours, enabled_guess)
            else:
                b.retry_attempt = str(val or "{}")

        elif key == "password":
            if val is not None and str(val).strip() != "":
                b.password = str(val)

        elif hasattr(b, key):
            setattr(b, key, val)

    b.save()
    return Response(
        {
            "success": True,
            "data": _business_to_response_dict(b),
            "message": "Business updated successfully.",
        }
    )


def _load_business_for_retry(request) -> tuple[Business | None, Response | None]:
    bid = _caller_business_id(request)
    if bid is None:
        return None, Response(
            {"success": False, "message": "Business not found for current user."},
            status=403,
        )
    b = Business.objects.filter(pk=bid).first()
    if not b:
        return None, Response(
            {"success": False, "message": "Business record not found."},
            status=404,
        )
    return b, None


@api_view(["GET", "POST"])
@permission_classes([IsAuthenticated])
def retry_attempts_collection(request):
    b, err = _load_business_for_retry(request)
    if err:
        return err

    if request.method == "GET":
        hours, enabled = _parse_retry_column(b.retry_attempt)
        data = [{"id": k, "hours": hours[k]} for k in sorted(hours.keys(), key=_retry_sort_key)]
        return Response({"success": True, "data": data, "enabled": enabled})

    try:
        hours_int = int(request.data.get("hours"))
    except (TypeError, ValueError):
        return Response({"success": False, "message": "hours must be an integer."}, status=400)

    if hours_int < 0 or hours_int > 168:
        return Response(
            {"success": False, "message": "hours must be between 0 and 168."},
            status=400,
        )

    cur, enabled = _parse_retry_column(b.retry_attempt)
    max_n = 0
    for k in cur:
        m = RETRY_KEY_RE.match(k)
        if m:
            max_n = max(max_n, int(m.group(1)))
    new_key = f"retry_attempt_{max_n + 1}"
    cur[new_key] = hours_int
    cur = _renumber_retry_keys(cur)
    b.retry_attempt = _persist_retry_column(cur, enabled)
    b.save(update_fields=["retry_attempt", "updated_at"])

    hours_after, _ = _parse_retry_column(b.retry_attempt)
    last_k = max(hours_after.keys(), key=_retry_sort_key) if hours_after else new_key
    return Response(
        {
            "success": True,
            "message": "Retry attempt created.",
            "data": {"id": last_k, "hours": hours_after.get(last_k, hours_int)},
        }
    )


@api_view(["PUT", "DELETE"])
@permission_classes([IsAuthenticated])
def retry_attempt_detail(request, attempt_id: str):
    if not RETRY_KEY_RE.match(attempt_id):
        return Response({"success": False, "message": "Invalid retry attempt id."}, status=400)

    b, err = _load_business_for_retry(request)
    if err:
        return err

    hours, enabled = _parse_retry_column(b.retry_attempt)
    if attempt_id not in hours:
        return Response({"success": False, "message": "Retry attempt not found."}, status=404)

    if request.method == "DELETE":
        del hours[attempt_id]
        hours = _renumber_retry_keys(hours)
        b.retry_attempt = _persist_retry_column(hours, enabled)
        b.save(update_fields=["retry_attempt", "updated_at"])
        return Response({"success": True, "message": "Retry attempt deleted."})

    try:
        new_hours = int(request.data.get("hours"))
    except (TypeError, ValueError):
        return Response({"success": False, "message": "hours must be an integer."}, status=400)

    if new_hours < 0 or new_hours > 168:
        return Response(
            {"success": False, "message": "hours must be between 0 and 168."},
            status=400,
        )

    hours[attempt_id] = new_hours
    hours = _renumber_retry_keys(hours)
    b.retry_attempt = _persist_retry_column(hours, enabled)
    b.save(update_fields=["retry_attempt", "updated_at"])
    return Response(
        {
            "success": True,
            "message": "Retry attempt updated.",
            "data": {"id": attempt_id, "hours": new_hours},
        }
    )


@api_view(["PUT"])
@permission_classes([IsAuthenticated])
def retry_attempts_toggle(request):
    b, err = _load_business_for_retry(request)
    if err:
        return err

    enabled = bool((request.data or {}).get("enabled"))
    hours, _ = _parse_retry_column(b.retry_attempt)
    b.retry_attempt = _persist_retry_column(hours, enabled)
    b.save(update_fields=["retry_attempt", "updated_at"])
    return Response(
        {"success": True, "enabled": enabled, "message": "Retry settings updated."}
    )


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def retry_attempts_status(request):
    b, err = _load_business_for_retry(request)
    if err:
        return err

    _, enabled = _parse_retry_column(b.retry_attempt)
    return Response({"success": True, "enabled": enabled})
