import json
import os
from collections import defaultdict
from datetime import datetime, timedelta

from flask import Blueprint, jsonify, request

from shared_db_utils import SCAPP_DATABASE_NAME, USER_DATABASE_NAME, get_db
from Analytic.Scapp_analytics import (
    compute_analytic_area_data,
    compute_analytic_area_versus_data,
    compute_analytics_by_region,
    compute_multiple_analytic_days,
)
from Analytic.centro_ids import build_centro_id
from Analytic.Scapp_feeder_score import (
    FEEDER_SCORE_COLLECTION_NAME,
    compute_feeder_score_by_area,
    compute_feeder_score_by_region,
    fetch_feeder_score_data,
)


agent_data_bp = Blueprint("agent_data", __name__)

# Guarda de seguridad, no un limite de negocio: el corte es un prefijo de `rows`, no una
# muestra, asi que truncar omite entidades enteras en vez de degradar de forma pareja.
# El calculo analitico ya se ejecuto completo antes del corte, de modo que un valor bajo no
# ahorra computo ni memoria pico: solo recorta lo que se serializa y se transfiere.
MAX_SNAPSHOT_ROWS = int(os.environ.get("SCAPP_MAX_SNAPSHOT_ROWS", "1000000"))
REGION_ANALYTICS_ROLES = {"gerencia", "superusuario"}


def _read_internal_secret():
    return os.environ.get("AGENT_INTERNAL_SHARED_SECRET", "").strip()


def _is_plain_object(value):
    return isinstance(value, dict)


def _normalize_text(value):
    return str(value or "").strip()


def _normalize_lookup_key(value):
    return _normalize_text(value).lower()


def _normalize_title_part(value):
    cleaned = _normalize_text(value)
    if not cleaned:
        return ""
    return "-".join(part.capitalize() for part in cleaned.split("-"))


def _build_center_lookup(region, area, centro):
    return "-".join(
        [
            _normalize_lookup_key(centro),
            _normalize_lookup_key(area),
            _normalize_lookup_key(region),
        ]
    )


def _build_center_display(region, area, centro):
    return build_centro_id(
        _normalize_title_part(region),
        _normalize_title_part(area),
        _normalize_title_part(centro),
    )


def _read_json_payload():
    payload = request.get_json(silent=True)
    return payload if isinstance(payload, dict) else {}


def _authorize_internal_request():
    configured_secret = _read_internal_secret()
    if not configured_secret:
        return None

    provided_secret = request.headers.get("X-Agent-Secret", "").strip()
    if provided_secret == configured_secret:
        return None

    return (
        jsonify(
            {
                "ok": False,
                "error": "forbidden",
                "detail": "Invalid internal agent secret.",
            }
        ),
        403,
    )


def _extract_query(payload):
    query = payload.get("query")
    return query if isinstance(query, dict) else {}


def _extract_view_context(payload):
    view_context = payload.get("viewContext")
    return view_context if isinstance(view_context, dict) else {}


def _extract_client_context(payload):
    client_context = payload.get("clientContext")
    return client_context if isinstance(client_context, dict) else {}


def _extract_username(payload):
    client_context = _extract_client_context(payload)
    visible_digest = payload.get("visibleDigest")
    identity = visible_digest.get("identity") if isinstance(visible_digest, dict) and isinstance(visible_digest.get("identity"), dict) else {}
    username = _normalize_text(
        client_context.get("username") or identity.get("username") or payload.get("username")
    )
    return username


def _load_user(username):
    client = get_db()
    users = client[USER_DATABASE_NAME].users
    return users.find_one({"username": username}) if username else None


def _flatten_user_center_permissions(user_document):
    permisos = user_document.get("permisos") if isinstance(user_document, dict) else {}
    allowed_centers = set()
    allowed_areas = set()
    allowed_regions = set()

    if not isinstance(permisos, dict):
        return allowed_centers, allowed_areas, allowed_regions

    for region, area_map in permisos.items():
        region_key = _normalize_lookup_key(region)
        if not isinstance(area_map, dict):
            continue
        if region_key:
            allowed_regions.add(region_key)
        for area, centers in area_map.items():
            area_key = _normalize_lookup_key(area)
            if not area_key:
                continue
            allowed_areas.add(f"{region_key}-{area_key}")
            if isinstance(centers, list):
                for center_id in centers:
                    center_key = _normalize_lookup_key(center_id)
                    if center_key:
                        allowed_centers.add(center_key)

    return allowed_centers, allowed_areas, allowed_regions


def _ensure_user_and_permissions(payload):
    username = _extract_username(payload)
    if not username:
        return None, None, (
            jsonify(
                {
                    "ok": False,
                    "error": "missing_username",
                    "detail": "A username is required in visibleDigest.identity or clientContext.",
                }
            ),
            400,
        )

    user_document = _load_user(username)
    if not user_document:
        return None, None, (
            jsonify(
                {
                    "ok": False,
                    "error": "unknown_user",
                    "detail": f"User '{username}' was not found.",
                }
            ),
            404,
        )

    return username, user_document, None


def _check_center_permission(user_document, region, area, centro):
    allowed_centers, _, _ = _flatten_user_center_permissions(user_document)
    return _build_center_lookup(region, area, centro) in allowed_centers


def _check_area_permission(user_document, region, area):
    _, allowed_areas, _ = _flatten_user_center_permissions(user_document)
    return f"{_normalize_lookup_key(region)}-{_normalize_lookup_key(area)}" in allowed_areas


def _check_region_permission(user_document, region):
    _, _, allowed_regions = _flatten_user_center_permissions(user_document)
    cargo = _normalize_lookup_key(user_document.get("cargo"))
    return cargo in REGION_ANALYTICS_ROLES or _normalize_lookup_key(region) in allowed_regions


def _resolve_scope_query(payload):
    query = _extract_query(payload)
    view_context = _extract_view_context(payload)
    entities = view_context.get("entities") if isinstance(view_context.get("entities"), dict) else {}
    filters = view_context.get("filters") if isinstance(view_context.get("filters"), dict) else {}
    dias = filters.get("dias") if isinstance(filters.get("dias"), dict) else {}

    return {
        "region": _normalize_text(query.get("region") or entities.get("region")),
        "area": _normalize_text(query.get("area") or entities.get("area")),
        "centro": _normalize_text(query.get("centro") or entities.get("centro")),
        "startDate": _normalize_text(query.get("startDate") or dias.get("startDate")),
        "endDate": _normalize_text(query.get("endDate") or dias.get("endDate")),
        "dayRange": query.get("dayRange") or dias.get("dayRange") or 1,
        "selectedDay": _normalize_text(query.get("selectedDay") or filters.get("selectedDay")),
        "entityName": _normalize_text(query.get("entityName")),
        "centers": query.get("centers") if isinstance(query.get("centers"), list) else [],
        "areas": query.get("areas") if isinstance(query.get("areas"), list) else [],
    }


def _parse_start_day(start_day_text):
    try:
        date_value = datetime.strptime(_normalize_text(start_day_text), "%d-%m-%Y")
        return datetime(date_value.year, date_value.month, date_value.day)
    except ValueError:
        return None


def _parse_day_range(value):
    try:
        parsed = int(value)
        return parsed if parsed > 0 else None
    except (TypeError, ValueError):
        return None


def _build_time_range(query):
    start_day = _parse_start_day(query.get("startDate"))
    day_range = _parse_day_range(query.get("dayRange"))
    if not start_day or not day_range:
        return None, None, (
            jsonify(
                {
                    "ok": False,
                    "error": "invalid_time_range",
                    "detail": "startDate must use DD-MM-YYYY and dayRange must be a positive integer.",
                }
            ),
            400,
        )

    end_day = start_day + timedelta(days=day_range - 1)
    return (
        start_day,
        {
            "startDate": query.get("startDate"),
            "endDate": end_day.strftime("%d-%m-%Y"),
            "dayRange": day_range,
        },
        None,
    )


def _normalize_metric_value(value):
    if not isinstance(value, dict):
        return {}

    next_value = dict(value)
    if "visitas_cout" in next_value and "visitas_count" not in next_value:
        next_value["visitas_count"] = next_value.pop("visitas_cout")
    if "perdidos_cout" in next_value and "perdidos_count" not in next_value:
        next_value["perdidos_count"] = next_value.pop("perdidos_cout")
    return next_value


def _normalize_values_summary(values_summary):
    if isinstance(values_summary, list):
        entries = values_summary
    elif isinstance(values_summary, dict):
        entries = [{"day": day, "value": value} for day, value in values_summary.items()]
    else:
        return []

    normalized = []
    for entry in entries:
        if not isinstance(entry, dict):
            continue
        day = _normalize_text(entry.get("day"))
        if not day:
            continue
        normalized.append({"day": day, "value": _normalize_metric_value(entry.get("value", {}))})

    return sorted(normalized, key=lambda item: item["day"])


def _read_alarm_band_value(value, key):
    band = value.get(key)
    if isinstance(band, dict):
        if band.get("value") is not None:
            return float(band.get("value") or 0)
        if band.get("alarm") is not None:
            return float(band.get("alarm") or 0)
    try:
        return float(band or 0)
    except (TypeError, ValueError):
        return 0.0


def _normalize_alarm_bands(value):
    if isinstance(value, list):
        entries = value
    elif isinstance(value, dict):
        entries = [{"day": day, "value": inner} for day, inner in value.items()]
    else:
        return []

    normalized = []
    for entry in entries:
        if not isinstance(entry, dict):
            continue
        day = _normalize_text(entry.get("day"))
        inner = entry.get("value")
        if not day or not isinstance(inner, dict):
            continue
        normalized.append(
            {
                "day": day,
                "baja": _read_alarm_band_value(inner, "baja"),
                "media": _read_alarm_band_value(inner, "media"),
                "alta": _read_alarm_band_value(inner, "alta"),
            }
        )

    return sorted(normalized, key=lambda item: item["day"])


def _normalize_time_series(entries):
    if not isinstance(entries, list):
        return []

    normalized = []
    for entry in entries:
        if not isinstance(entry, dict):
            continue
        day = _normalize_text(entry.get("day"))
        values = entry.get("value")
        if not day or not isinstance(values, list):
            continue
        normalized.append({"day": day, "value": values})
    return normalized


def _pick_graph_activity(node):
    series = []
    for key, value in node.items():
        if key.startswith("graph_activity") and isinstance(value, dict):
            series.append(
                {
                    "key": key,
                    "name": _normalize_text(value.get("name") or key),
                    "data": value.get("data") if isinstance(value.get("data"), list) else [],
                }
            )
    return series


def _collect_nested_entities(analytic_data, scope_label="root"):
    entities = []

    def walk(node, path):
        if not isinstance(node, dict):
            return

        if any(key in node for key in ("values_summary", "alarms_by_tasa", "tasa_summary")):
            entity_name = _normalize_text(node.get("name") or (path[-1] if path else scope_label) or scope_label)
            entities.append(
                {
                    "entityId": ":".join(path) or entity_name or scope_label,
                    "entityName": entity_name,
                    "valuesSummary": _normalize_values_summary(node.get("values_summary")),
                    "alarmBands": _normalize_alarm_bands(node.get("alarms_by_tasa") or node.get("tasa_summary")),
                    "timeline": {
                        "graphActivity": _pick_graph_activity(node),
                        "graphFeeding": _normalize_time_series(node.get("graph_feeding")),
                        "graphPerdidos": _normalize_time_series(node.get("graph_perdidos")),
                        "tasaValues": _normalize_time_series(node.get("tasa_values")),
                        "alarmsData": _normalize_time_series(node.get("alarms_data")),
                    },
                }
            )
            return

        for key, value in node.items():
            if isinstance(value, dict):
                walk(value, path + [key])

    walk(analytic_data if isinstance(analytic_data, dict) else {}, [])
    return entities


def _build_daily_rows(entities, scope_name):
    rows = []
    for entity in entities:
        alarm_by_day = {item["day"]: item for item in entity.get("alarmBands", [])}
        for item in entity.get("valuesSummary", []):
            row = {
                "scope": scope_name,
                "entityName": entity.get("entityName"),
                "entityId": entity.get("entityId"),
                "day": item.get("day"),
            }
            row.update(item.get("value", {}))
            alarm_row = alarm_by_day.get(item.get("day"))
            if alarm_row:
                row["alarm_band_baja"] = alarm_row.get("baja")
                row["alarm_band_media"] = alarm_row.get("media")
                row["alarm_band_alta"] = alarm_row.get("alta")
            rows.append(row)
    return rows


def _build_snapshot(schema, source, query, entity_scope, rows, extra=None):
    limited_rows = rows[:MAX_SNAPSHOT_ROWS]
    columns = sorted({key for row in limited_rows for key in row.keys()})
    entity_names = sorted({row.get("entityName") for row in limited_rows if row.get("entityName")})
    day_values = sorted({row.get("day") for row in limited_rows if row.get("day")})
    stats = {
        "rowCount": len(rows),
        "returnedRowCount": len(limited_rows),
        "entityCount": len(entity_names),
        "dayCount": len(day_values),
    }

    snapshot = {
        "schema": schema,
        "source": source,
        "query": query,
        "rows": limited_rows,
        "columns": columns,
        "timeRange": {
            "startDate": query.get("startDate"),
            "endDate": query.get("endDate"),
            "dayRange": query.get("dayRange"),
        },
        "entityScope": entity_scope,
        "stats": stats,
        "truncated": len(rows) > len(limited_rows),
        "rowCount": len(rows),
        "sample": limited_rows[:20],
    }

    if isinstance(extra, dict) and extra:
        snapshot.update(extra)

    return snapshot


def _build_highlights(rows):
    if not rows:
        return []

    numeric_keys = [
        "alarms_count",
        "perdidos_count",
        "visitas_count",
        "sfr_value",
        "alarms_by_ton",
        "alarms_by_visits",
    ]
    highlights = []
    for key in numeric_keys:
        ranked = [row for row in rows if isinstance(row.get(key), (int, float))]
        if not ranked:
            continue
        top = max(ranked, key=lambda row: row.get(key) or 0)
        highlights.append(
            {
                "metric": key,
                "entityName": top.get("entityName"),
                "day": top.get("day"),
                "value": top.get(key),
            }
        )
    return highlights[:8]


def _build_feeder_score_rows(feeder_score_data, scope_name):
    rows = []

    def walk(day, node, path):
        if not isinstance(node, dict):
            return

        if any(key in node for key in ("score", "status", "scope", "n_jaulas_scored")):
            row = {
                "scope": scope_name,
                "day": day,
                "entityId": ":".join(path),
                "entityName": path[-1] if path else scope_name,
                "feederScope": node.get("scope"),
            }
            for key, value in node.items():
                if isinstance(value, (str, int, float, bool)) or value is None:
                    row[key] = value
            reaction_time = node.get("reaction_time")
            if isinstance(reaction_time, dict):
                for key in ("min", "avg", "mean_sec", "p95_sec", "max_sec", "max", "n"):
                    if isinstance(reaction_time.get(key), (int, float)):
                        row[f"reaction_time_{key}"] = reaction_time.get(key)
            rows.append(row)
            return

        for key, value in node.items():
            if isinstance(value, dict):
                walk(day, value, path + [str(key)])

    for day, payload in (feeder_score_data or {}).items():
        walk(day, payload, [])
    return rows


def _build_time_window_rows(entity, selected_day):
    rows = []
    timeline = entity.get("timeline", {})

    for series_name in ("graphFeeding", "graphPerdidos", "tasaValues"):
        for series in timeline.get(series_name, []):
            if selected_day and series.get("day") != selected_day:
                continue
            for point in series.get("value", []):
                row = {
                    "entityName": entity.get("entityName"),
                    "entityId": entity.get("entityId"),
                    "day": series.get("day"),
                    "series": series_name,
                }
                if isinstance(point, dict):
                    row.update(point)
                rows.append(row)

    for series in timeline.get("alarmsData", []):
        if selected_day and series.get("day") != selected_day:
            continue
        for point in series.get("value", []):
            row = {
                "entityName": entity.get("entityName"),
                "entityId": entity.get("entityId"),
                "day": series.get("day"),
                "series": "alarmsData",
            }
            if isinstance(point, dict):
                row.update(point)
            rows.append(row)

    for activity in timeline.get("graphActivity", []):
        for point in activity.get("data", []):
            row = {
                "entityName": entity.get("entityName"),
                "entityId": entity.get("entityId"),
                "day": selected_day or None,
                "series": activity.get("key"),
                "activityLabel": activity.get("name"),
            }
            if isinstance(point, dict):
                row.update(point)
            rows.append(row)

    return rows


def _pick_entity_for_time_window(entities, entity_name):
    if not entities:
        return None
    if not entity_name:
        return entities[0]

    entity_name_lower = _normalize_lookup_key(entity_name)
    for entity in entities:
        if _normalize_lookup_key(entity.get("entityName")) == entity_name_lower:
            return entity
    return entities[0]


@agent_data_bp.route("/internal/agent-data/scapp/schema", methods=["POST"])
def scapp_schema():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    _, _, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    rows = [
        {
            "metric": "delivered_food",
            "label": "Alimento entregado",
            "aggregation": "sum",
        },
        {
            "metric": "sfr_value",
            "label": "SFR",
            "aggregation": "average",
        },
        {
            "metric": "alarms_count",
            "label": "Alarmas",
            "aggregation": "sum",
        },
        {
            "metric": "perdidos_count",
            "label": "PPA",
            "aggregation": "sum",
        },
        {
            "metric": "visitas_count",
            "label": "Visitas",
            "aggregation": "sum",
        },
        {
            "metric": "alarms_by_ton",
            "label": "Alarmas por tonelada",
            "aggregation": "sum_of_visible_daily_metric",
        },
        {
            "metric": "alarms_by_visits",
            "label": "Alarmas por visita",
            "aggregation": "sum_of_visible_daily_metric",
            "formulaConfirmed": False,
        },
        {
            "metric": "feeder_score.score",
            "label": "Puntuacion de alimentador",
            "aggregation": "average",
        },
        {
            "metric": "feeder_score.Q",
            "label": "Calidad FeederScore",
            "aggregation": "average",
        },
        {
            "metric": "feeder_score.sfr_bonus",
            "label": "Bono SFR FeederScore",
            "aggregation": "average",
        },
        {
            "metric": "feeder_score.porc_entregado",
            "label": "Porcentaje entregado",
            "aggregation": "average",
        },
        {
            "metric": "feeder_score.reaction_time_avg",
            "label": "Tiempo de reaccion promedio",
            "aggregation": "average",
        },
    ]
    snapshot = {
        "schema": "scapp.schema.v1",
        "source": "internal.agent.schema",
        "query": {},
        "rows": rows,
        "columns": ["metric", "label", "aggregation", "formulaConfirmed"],
        "timeRange": None,
        "entityScope": {"kind": "schema"},
        "stats": {"rowCount": len(rows), "returnedRowCount": len(rows), "entityCount": 0, "dayCount": 0},
        "truncated": False,
        "rowCount": len(rows),
        "sample": [],
    }
    snapshot["sample"] = snapshot["rows"][:20]

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/center-analytics", methods=["POST"])
def scapp_center_analytics():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    area = query["area"]
    centro = query["centro"]

    if not all([region, area, centro]):
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region, area and centro are required."}), 400

    if not _check_center_permission(user_document, region, area, centro):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to {centro}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        raw_data = compute_multiple_analytic_days(
            _build_center_display(region, area, centro),
            start_day,
            time_range["dayRange"],
            {},
            True,
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    entities = _collect_nested_entities(raw_data.get("analytic_data", raw_data), centro)
    rows = _build_daily_rows(entities, "center")
    snapshot = _build_snapshot(
        "scapp.analytics.center.v1",
        "internal.agent.center",
        time_range,
        {
            "kind": "center",
            "region": region,
            "area": area,
            "centro": centro,
        },
        rows,
        {
            "highlights": _build_highlights(rows),
            "entityNames": [entity.get("entityName") for entity in entities],
        },
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/area-analytics", methods=["POST"])
def scapp_area_analytics():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    area = query["area"]
    if not all([region, area]):
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region and area are required."}), 400

    if not _check_area_permission(user_document, region, area):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to area {area}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        raw_data = compute_analytic_area_data(
            f"{_normalize_title_part(region)}-{_normalize_title_part(area)}",
            start_day,
            time_range["dayRange"],
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    entities = _collect_nested_entities(raw_data, area)
    rows = _build_daily_rows(entities, "area")
    snapshot = _build_snapshot(
        "scapp.analytics.area.v1",
        "internal.agent.area",
        time_range,
        {
            "kind": "area",
            "region": region,
            "area": area,
        },
        rows,
        {
            "highlights": _build_highlights(rows),
            "entityNames": [entity.get("entityName") for entity in entities],
        },
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/region-analytics", methods=["POST"])
def scapp_region_analytics():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    if not region:
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region is required."}), 400

    if not _check_region_permission(user_document, region):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to region {region}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        client = get_db()
        scapp_db = client[SCAPP_DATABASE_NAME]
        raw_data = compute_analytics_by_region(
            scapp_db,
            _normalize_title_part(region),
            start_day,
            time_range["dayRange"],
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    entities = _collect_nested_entities(raw_data, region)
    rows = _build_daily_rows(entities, "region")
    snapshot = _build_snapshot(
        "scapp.analytics.region.v1",
        "internal.agent.region",
        time_range,
        {
            "kind": "region",
            "region": region,
        },
        rows,
        {
            "highlights": _build_highlights(rows),
            "entityNames": [entity.get("entityName") for entity in entities],
        },
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/feeder-score-center", methods=["POST"])
def scapp_feeder_score_center():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    area = query["area"]
    centro = query["centro"]

    if not all([region, area, centro]):
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region, area and centro are required."}), 400

    if not _check_center_permission(user_document, region, area, centro):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to {centro}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        client = get_db()
        scapp_db = client[SCAPP_DATABASE_NAME]
        feeder_score = fetch_feeder_score_data(
            scapp_db[FEEDER_SCORE_COLLECTION_NAME],
            _build_center_display(region, area, centro),
            start_day,
            start_day + timedelta(days=time_range["dayRange"]),
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    rows = _build_feeder_score_rows(feeder_score, "feeder-score-center")
    snapshot = _build_snapshot(
        "scapp.feeder-score.center.v1",
        "internal.agent.feeder-score-center",
        time_range,
        {"kind": "center", "region": region, "area": area, "centro": centro},
        rows,
    )
    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/feeder-score-area", methods=["POST"])
def scapp_feeder_score_area():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    area = query["area"]

    if not all([region, area]):
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region and area are required."}), 400

    if not _check_area_permission(user_document, region, area):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to area {area}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        client = get_db()
        scapp_db = client[SCAPP_DATABASE_NAME]
        feeder_score = compute_feeder_score_by_area(
            scapp_db,
            f"{_normalize_title_part(region)}-{_normalize_title_part(area)}",
            start_day,
            time_range["dayRange"],
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    rows = _build_feeder_score_rows(feeder_score, "feeder-score-area")
    snapshot = _build_snapshot(
        "scapp.feeder-score.area.v1",
        "internal.agent.feeder-score-area",
        time_range,
        {"kind": "area", "region": region, "area": area},
        rows,
    )
    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/feeder-score-region", methods=["POST"])
def scapp_feeder_score_region():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    if not region:
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region is required."}), 400

    if not _check_region_permission(user_document, region):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to region {region}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        client = get_db()
        scapp_db = client[SCAPP_DATABASE_NAME]
        feeder_score = compute_feeder_score_by_region(
            scapp_db,
            _normalize_title_part(region),
            start_day,
            time_range["dayRange"],
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    rows = _build_feeder_score_rows(feeder_score, "feeder-score-region")
    snapshot = _build_snapshot(
        "scapp.feeder-score.region.v1",
        "internal.agent.feeder-score-region",
        time_range,
        {"kind": "region", "region": region},
        rows,
    )
    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/compare-centers", methods=["POST"])
def scapp_compare_centers():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    center_ids = [_normalize_text(item) for item in query.get("centers", []) if _normalize_text(item)]
    if len(center_ids) < 2:
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "At least two centers are required."}), 400

    allowed_centers, _, _ = _flatten_user_center_permissions(user_document)
    denied = [item for item in center_ids if _normalize_lookup_key(item) not in allowed_centers]
    if denied:
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to: {', '.join(denied)}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        rows = []
        for center_id in center_ids:
            raw_data = compute_multiple_analytic_days(center_id, start_day, time_range["dayRange"], {}, True)
            entities = _collect_nested_entities(raw_data.get("analytic_data", raw_data), center_id)
            for row in _build_daily_rows(entities, "compare-centers"):
                row["comparisonTarget"] = center_id
                rows.append(row)
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    snapshot = _build_snapshot(
        "scapp.analytics.compare-centers.v1",
        "internal.agent.compare-centers",
        {**time_range, "centers": center_ids},
        {"kind": "compare-centers"},
        rows,
        {"highlights": _build_highlights(rows)},
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/compare-areas", methods=["POST"])
def scapp_compare_areas():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    area_ids = [_normalize_text(item) for item in query.get("areas", []) if _normalize_text(item)]
    if len(area_ids) < 2:
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "At least two areas are required."}), 400

    _, allowed_areas, _ = _flatten_user_center_permissions(user_document)
    denied = [item for item in area_ids if _normalize_lookup_key(item) not in allowed_areas]
    if denied:
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to: {', '.join(denied)}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        client = get_db()
        scapp_db = client[SCAPP_DATABASE_NAME]
        collection = scapp_db["data_analytic"]
        rows = []
        for area_id in area_ids:
            raw_data = compute_analytic_area_versus_data(collection, area_id, start_day, time_range["dayRange"])
            entities = _collect_nested_entities(raw_data, area_id)
            for row in _build_daily_rows(entities, "compare-areas"):
                row["comparisonTarget"] = area_id
                rows.append(row)
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    snapshot = _build_snapshot(
        "scapp.analytics.compare-areas.v1",
        "internal.agent.compare-areas",
        {**time_range, "areas": area_ids},
        {"kind": "compare-areas"},
        rows,
        {"highlights": _build_highlights(rows)},
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200


@agent_data_bp.route("/internal/agent-data/scapp/live-summary", methods=["POST"])
def scapp_live_summary():
    return jsonify({
        "ok": False,
        "error": "gone",
        "detail": "Live summary fue dado de baja el 2026-07-29.",
    }), 410


@agent_data_bp.route("/internal/agent-data/scapp/time-window", methods=["POST"])
def scapp_time_window():
    auth_error = _authorize_internal_request()
    if auth_error:
        return auth_error

    payload = _read_json_payload()
    username, user_document, user_error = _ensure_user_and_permissions(payload)
    if user_error:
        return user_error

    trace_id = _normalize_text(payload.get("traceId"))
    query = _resolve_scope_query(payload)
    region = query["region"]
    area = query["area"]
    centro = query["centro"]
    if not all([region, area, centro]):
        return jsonify({"ok": False, "error": "invalid_scope", "detail": "region, area and centro are required."}), 400

    if not _check_center_permission(user_document, region, area, centro):
        return jsonify({"ok": False, "error": "forbidden", "detail": f"User '{username}' has no Scapp access to {centro}."}), 403

    start_day, time_range, time_error = _build_time_range(query)
    if time_error:
        return time_error

    try:
        raw_data = compute_multiple_analytic_days(
            _build_center_display(region, area, centro),
            start_day,
            time_range["dayRange"],
            {},
            True,
        )
    except Exception as error:
        return jsonify({"ok": False, "error": "backend_failure", "detail": str(error)}), 500

    entities = _collect_nested_entities(raw_data.get("analytic_data", raw_data), centro)
    entity = _pick_entity_for_time_window(entities, query.get("entityName"))
    if not entity:
        return jsonify({"ok": False, "error": "empty", "detail": "No timeline data was found for the current selection."}), 404

    rows = _build_time_window_rows(entity, query.get("selectedDay"))
    snapshot = _build_snapshot(
        "scapp.analytics.time-window.v1",
        "internal.agent.time-window",
        {**time_range, "selectedDay": query.get("selectedDay"), "entityName": entity.get("entityName")},
        {
            "kind": "time-window",
            "region": region,
            "area": area,
            "centro": centro,
            "entityName": entity.get("entityName"),
        },
        rows,
    )

    return jsonify({"ok": True, "traceId": trace_id, "snapshot": snapshot}), 200
