from __future__ import annotations

import json
import random
import string
from datetime import datetime, timedelta, timezone
from typing import Any

from django.db import DatabaseError, connections
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response


def _q_ident(name: str) -> str:
    # MySQL identifier quoting; supports digit-prefixed table names.
    return f"`{name.replace('`', '``')}`"


def _require_int(value: Any, default: int) -> int:
    try:
        v = int(value)
        return v if v > 0 else default
    except Exception:
        return default


def _rand_token(prefix: str, n: int = 12) -> str:
    alphabet = string.ascii_lowercase + string.digits
    return prefix + "".join(random.choice(alphabet) for _ in range(n))


def _dummy_payload(bid: int) -> dict[str, Any]:
    now = datetime.now(timezone.utc).replace(microsecond=0)
    start = now - timedelta(seconds=random.randint(30, 600))
    end = start + timedelta(seconds=random.randint(10, 240))
    gid = random.randint(1, 10)
    return {
        "callid": _rand_token("call_", 16),
        "calid": _rand_token("cal_", 10),
        "refid": _rand_token("ref_", 10),
        "bid": bid,
        "gid": gid,
        "groupname": f"Group {gid}",
        "agentname": f"Agent {random.randint(1, 50)}",
        "assignto": f"Agent {random.randint(1, 50)}",
        "eid": random.randint(1, 5000),
        "source": "mcube",
        "landingnumber": "1800123456",
        "hid": random.randint(1, 100000),
        "callfrom": f"+91{random.randint(6000000000, 9999999999)}",
        "callto": f"+91{random.randint(6000000000, 9999999999)}",
        "agi_uniqueid": _rand_token("agi_", 14),
        "agi_linkedid": _rand_token("link_", 14),
        "starttime": start,
        "endtime": end,
        "pulse": max(0, int((end - start).total_seconds() // 60)),
        "mpulse": 0,
        "status": "completed",
        "dialstatus": "ANSWERED",
        "direction": random.choice(["inbound", "outbound"]),
        "abandoned": 0,
        "created_on": now,
        "updated_on": now,
    }


def _parse_body(request) -> dict[str, Any]:
    """
    DRF parses JSON into request.data for application/json.
    For safety, also handle raw bodies.
    """
    if isinstance(getattr(request, "data", None), dict):
        return dict(request.data)
    raw = getattr(request, "body", b"") or b""
    if not raw:
        return {}
    try:
        decoded = raw.decode("utf-8", errors="ignore").strip()
        if not decoded:
            return {}
        obj = json.loads(decoded)
        return obj if isinstance(obj, dict) else {}
    except Exception:
        return {}


def _table_columns(cursor, table: str) -> set[str]:
    cursor.execute(
        """
        SELECT COLUMN_NAME
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s
        """,
        [table],
    )
    return {str(r[0]) for r in (cursor.fetchall() or []) if r and r[0]}


def _coerce_dt(v: Any) -> Any:
    """
    If the client sends 'YYYY-MM-DD HH:MM:SS', keep it as-is for MySQL DATETIME.
    If a datetime object is passed, keep it.
    """
    if v is None:
        return None
    if isinstance(v, datetime):
        return v
    s = str(v).strip()
    return s if s else None


@api_view(["GET", "POST"])
@permission_classes([AllowAny])
def mcube_data(request):
    """
    Global URL: /api/mcube-data

    - bid is OPTIONAL.
      Priority: ?bid= (query) -> JSON body {"bid": ...} -> default 2000

    Target table: `{bid}_mcubecallhistory` in the `cluster` DB.
    """
    body = _parse_body(request)
    bid = _require_int(request.query_params.get("bid"), default=0)
    if bid <= 0:
        bid = _require_int(body.get("bid") if isinstance(body, dict) else None, default=2000)

    table = f"{bid}_mcubecallhistory"

    payload = body if body else _dummy_payload(bid)
    payload.setdefault("bid", bid)
    payload.pop("call_history_id", None)  # auto-increment PK

    # Normalize common datetime fields when present.
    for key in (
        "starttime",
        "endtime",
        "answeredtime",
        "customer_answeredtime",
        "last_modified",
        "created_on",
        "updated_on",
        "exe_ring_sttime",
        "exe_ring_endtime",
        "cust_ring_sttime",
        "cust_ring_endtime",
        "exe_answer",
        "cust_answer",
        "holdStarttime",
        "dispersmenttime",
        "queuesttime",
        "queueendtime",
    ):
        if key in payload:
            payload[key] = _coerce_dt(payload.get(key))

    try:
        conn = connections["cluster"]
        with conn.cursor() as cursor:
            cols = _table_columns(cursor, table)
            if not cols:
                return Response(
                    {"success": False, "message": f"Table not found: {table}", "table": table},
                    status=404,
                )

            insert_cols = [k for k in payload.keys() if k in cols]
            if not insert_cols:
                return Response(
                    {
                        "success": False,
                        "message": "No valid columns found in payload for this table.",
                        "table": table,
                    },
                    status=400,
                )

            values = [payload[c] for c in insert_cols]
            col_sql = ", ".join(_q_ident(c) for c in insert_cols)
            ph = ", ".join(["%s"] * len(insert_cols))
            sql = f"INSERT INTO {_q_ident(table)} ({col_sql}) VALUES ({ph})"
            cursor.execute(sql, values)
            inserted_id = cursor.lastrowid
    except DatabaseError as e:
        return Response({"success": False, "message": str(e), "table": table}, status=500)

    return Response(
        {
            "success": True,
            "table": table,
            "inserted_id": inserted_id,
            "inserted_columns": insert_cols,
        }
    )

