"""Internal inter-app client built on top of the platform gateway.

This module lets one app call another through the same gateway flow used by
external requests, without issuing HTTP requests and without direct app-to-app
imports.
"""

from __future__ import annotations

from contextlib import contextmanager, nullcontext
from dataclasses import dataclass, field
from types import SimpleNamespace
from typing import Any
from uuid import uuid4

from flask import Flask, Response, current_app, has_app_context

from platform.gateway.api_gateway import APIGateway
from platform.logging.platform_logger import get_platform_logger, log_structured


@dataclass(slots=True)
class _InternalRequest:
    """Minimal request adapter compatible with the current gateway contract."""

    headers: dict[str, str]
    path: str
    method: str = "POST"
    remote_addr: str = "127.0.0.1"
    host: str = "internal.absolutems"
    json: dict[str, Any] = field(default_factory=dict)
    args: dict[str, Any] = field(default_factory=dict)

    def get_json(self, silent: bool = False) -> dict[str, Any] | None:
        return self.json


def call_app(context, app: str, action: str, payload: dict):
    """Call a target app through the internal gateway flow.

    The caller provides the current request context. The inter-app layer reuses
    the gateway rather than importing the target app directly.
    """
    logger = get_platform_logger("interapp")
    request_id = _request_id_from_context(context)
    caller_app = _caller_app_from_context(context)

    log_structured(
        logger,
        "interapp_call",
        caller_app=caller_app,
        target_app=app,
        action=action,
        request_id=request_id,
    )

    gateway = APIGateway()
    fake_request = _InternalRequest(
        headers={
            "X-Tenant-ID": _tenant_id_from_context(context),
            "X-App-ID": app,
            "X-User-ID": _user_id_from_context(context),
            "X-Request-ID": request_id,
        },
        path=f"/api/v1/{app}/{action}",
        json=payload or {},
    )

    try:
        with _app_context():
            result = gateway.handle(fake_request, app, action)
        return _normalize_gateway_result(result, app=app)
    except Exception as exc:
        log_structured(
            logger,
            "interapp_error",
            caller_app=caller_app,
            target_app=app,
            action=action,
            request_id=request_id,
            message=str(exc),
        )
        return {
            "status": "error",
            "app": app,
            "message": str(exc),
        }


def _normalize_gateway_result(result, *, app: str) -> dict[str, Any]:
    if isinstance(result, tuple):
        response, status_code = result
        payload = _response_to_json(response)
        if isinstance(payload, dict):
            payload.setdefault("app", app)
            payload.setdefault("http_status", status_code)
            return payload
        return {"status": "error", "app": app, "message": f"Unexpected response payload: {payload!r}"}

    payload = _response_to_json(result)
    if isinstance(payload, dict):
        payload.setdefault("app", app)
        return payload

    if isinstance(result, dict):
        result.setdefault("app", app)
        return result

    return {
        "status": "error",
        "app": app,
        "message": f"Unsupported gateway response type: {type(result).__name__}",
    }


def _response_to_json(response):
    if isinstance(response, Response):
        return response.get_json(silent=True)
    return response


def _tenant_id_from_context(context) -> str:
    tenant = (context or {}).get("tenant") or {}
    tenant_id = tenant.get("tenant_id") or (context or {}).get("tenant_id")
    return str(tenant_id or "absolutems")


def _user_id_from_context(context) -> str:
    user_id = (context or {}).get("user_id")
    return "" if user_id is None else str(user_id)


def _request_id_from_context(context) -> str:
    return str((context or {}).get("request_id") or uuid4())


def _caller_app_from_context(context) -> str:
    return str((context or {}).get("app_id") or (context or {}).get("app") or "unknown")


@contextmanager
def _app_context():
    if has_app_context():
        with nullcontext():
            yield
        return

    temp_app = Flask("platform_interapp")
    with temp_app.app_context():
        yield
