"""Platform Standard v1 entrypoint for centralized SMS and email delivery."""

from __future__ import annotations

from bridge_platform.apps.contracts import ActionContract, AppManifest, WordPressPackageContract
from bridge_platform.apps.envelopes import error_envelope, success_envelope
from bridge_platform.secrets.secrets_manager import SecretsManager

from .drivers import default_registry
from .repository import MessagingRepository
from .service import MessagingError, MessagingService, _delivery_data, _requesting_app, _tenant_id


SEND_INPUT = {
    "type": "object", "additionalProperties": False,
    "required": ["channel", "recipient", "template_key", "template_data", "purpose", "idempotency_key"],
    "properties": {
        "channel": {"type": "string", "enum": ["sms", "email"]},
        "recipient": {"type": "object", "additionalProperties": False,
                      "properties": {"user_id": {"type": ["string", "null"]}, "phone": {"type": ["string", "null"]}, "email": {"type": ["string", "null"]}}},
        "template_key": {"type": "string", "minLength": 1, "maxLength": 128},
        "template_data": {"type": "object"}, "purpose": {"type": "string", "minLength": 1, "maxLength": 64},
        "idempotency_key": {"type": "string", "minLength": 8, "maxLength": 128},
        "metadata": {"type": "object", "additionalProperties": False,
                     "properties": {"requested_by_app": {"type": "string"}, "correlation_id": {"type": "string"},
                                    "resource_type": {"type": ["string", "null"]}, "resource_id": {"type": ["string", "null"]}}},
    },
}


APP_MANIFEST = AppManifest(
    app_id="messaging_service", display_name="Messaging Service", app_version="1.0.0",
    description="Centralized tenant-aware template delivery over SMS and email.",
    actions={
        "health_v1": ActionContract(capability="platform.health", description="Report local messaging runtime health.",
                                    output_schema={"type": "object", "required": ["healthy", "drivers"]}),
        "send_template_v1": ActionContract(capability="messaging.template.send", description="Render and deliver an authorized message template.",
            input_schema=SEND_INPUT, output_schema={"type": "object", "required": ["delivery_id", "channel", "status", "created_at"]},
            side_effects=True, idempotent=False, idempotency_key_required=True, timeout_seconds=30, data_classification="restricted",
            errors=("invalid_input", "invalid_recipient", "permission_denied", "template_not_found", "template_invalid",
                    "provider_unavailable", "rate_limit_exceeded", "tenant_context_missing", "caller_context_missing")),
        "get_delivery_status_v1": ActionContract(capability="messaging.delivery.status.read", description="Read one tenant-isolated delivery status.",
            input_schema={"type": "object", "additionalProperties": False, "required": ["delivery_id"], "properties": {"delivery_id": {"type": "string"}}},
            output_schema={"type": "object", "required": ["delivery_id", "channel", "status", "created_at"]}, data_classification="confidential",
            errors=("invalid_input", "delivery_not_found", "permission_denied")),
        "health_check_provider_v1": ActionContract(capability="messaging.provider.health.check", description="Run a bounded provider connectivity check.",
            input_schema={"type": "object", "additionalProperties": False, "required": ["provider_id"], "properties": {"provider_id": {"type": "string"}}},
            output_schema={"type": "object", "required": ["provider_id", "status", "connected", "checked_at"]}, timeout_seconds=20,
            errors=("provider_not_found", "permission_denied")),
        "configure_provider_v1": ActionContract(capability="messaging.provider.configure", description="Create or update a provider and optionally replace its write-only secret.",
            input_schema={"type": "object", "required": ["name", "driver", "channel", "config"]},
            output_schema={"type": "object", "required": ["provider_id", "secret_configured"]}, side_effects=True, idempotent=True,
            data_classification="restricted", errors=("permission_denied", "invalid_configuration")),
        "configure_permission_v1": ActionContract(capability="messaging.permission.configure", description="Configure one trusted caller's messaging allowlist.",
            input_schema={"type": "object", "required": ["requesting_app", "channels", "templates", "purposes"]},
            output_schema={"type": "object", "required": ["requesting_app", "enabled"]}, side_effects=True, idempotent=True,
            data_classification="restricted", errors=("permission_denied", "invalid_input")),
    },
    wordpress_package=WordPressPackageContract(package_id="messaging_service_wp", version="1.0.0-dev",
        source_directory="wordpress-pack/messaging-service-wp", entrypoint="messaging-service-wp.php",
        plugin_file="messaging-service-wp/messaging-service-wp.php",
        capabilities=("messaging.template.send", "messaging.delivery.status.read")),
)


def handle_request(context, action):
    request_id = str((context or {}).get("request_id") or "")
    try:
        service = MessagingService()
        payload = (context or {}).get("payload") or {}
        if action == "health_v1":
            return success_envelope(app_id=APP_MANIFEST.app_id, action=action, request_id=request_id,
                                    data={"healthy": True, "drivers": list(default_registry().names()), "provider_checked": False})
        if action == "send_template_v1":
            data = service.send_template(context, payload)
        elif action == "get_delivery_status_v1":
            delivery = service.repository.delivery(_tenant_id(context), str(payload.get("delivery_id") or ""))
            if not delivery: raise MessagingError("delivery_not_found", "Delivery was not found.", status=404)
            if delivery.requesting_app != _requesting_app(context) and not _is_admin(context):
                raise MessagingError("permission_denied", "Delivery belongs to another caller.", status=403)
            data = _delivery_data(delivery)
        elif action == "health_check_provider_v1":
            _require_admin(context); data = service.health_check(context, str(payload.get("provider_id") or ""))
        elif action == "configure_provider_v1":
            _require_admin(context); data = _configure_provider(context, payload, service)
        elif action == "configure_permission_v1":
            _require_admin(context); data = _configure_permission(context, payload, service.repository)
        else:
            return error_envelope(app_id=APP_MANIFEST.app_id, action=action, request_id=request_id,
                                  code="action_not_found", message="The requested action is not supported."), 404
        return success_envelope(app_id=APP_MANIFEST.app_id, action=action, request_id=request_id, data=data)
    except MessagingError as exc:
        return error_envelope(app_id=APP_MANIFEST.app_id, action=action, request_id=request_id,
                              code=exc.code, message=str(exc), retryable=exc.retryable), exc.status
    except (ValueError, LookupError) as exc:
        return error_envelope(app_id=APP_MANIFEST.app_id, action=action, request_id=request_id,
                              code="invalid_configuration", message=str(exc)), 400


def _configure_provider(context, payload, service):
    config = payload.get("config") or {}
    service.registry.get(str(payload.get("driver"))).validate_configuration(config)
    tenant_id = _tenant_id(context)
    secret_name = str(payload.get("secret_ref") or f'provider.{payload.get("name", "provider")}.credential').lower().replace(" ", "_")
    if payload.get("secret"):
        SecretsManager().put_secret(tenant_id=tenant_id, app_id=APP_MANIFEST.app_id,
                                    secret_name=secret_name, secret_value=str(payload["secret"]), actor_id=str(context.get("user_id") or ""))
    values = {"provider_id": payload.get("provider_id"), "name": str(payload["name"]), "driver": str(payload["driver"]),
              "channel": str(payload["channel"]), "config_json": config, "secret_ref": secret_name,
              "enabled": bool(payload.get("enabled", True)), "priority": int(payload.get("priority", 100)),
              "daily_limit": payload.get("daily_limit"), "per_minute_limit": payload.get("per_minute_limit"),
              "max_retries": int(payload.get("max_retries", 2))}
    provider = service.repository.save_provider(tenant_id, values)
    configured = bool(SecretsManager().get_secret(tenant_id=tenant_id, app_id=APP_MANIFEST.app_id, secret_name=secret_name))
    return {"provider_id": provider.provider_id, "name": provider.name, "driver": provider.driver,
            "channel": provider.channel, "enabled": provider.enabled, "secret_configured": configured}


def _configure_permission(context, payload, repository):
    values = {"requesting_app": str(payload["requesting_app"]), "enabled": bool(payload.get("enabled", True)),
              "channels_json": list(payload.get("channels") or []), "templates_json": list(payload.get("templates") or []),
              "purposes_json": list(payload.get("purposes") or []), "allow_fallback": bool(payload.get("allow_fallback", False))}
    record = repository.save_permission(_tenant_id(context), values)
    return {"requesting_app": record.requesting_app, "enabled": record.enabled, "channels": record.channels_json,
            "templates": record.templates_json, "purposes": record.purposes_json, "allow_fallback": record.allow_fallback}


def _is_admin(context) -> bool:
    identity = (context or {}).get("identity")
    return bool(identity and ({"admin", "administrator", "platform_admin"} & set(getattr(identity, "roles", ()))))


def _require_admin(context) -> None:
    if not _is_admin(context): raise MessagingError("permission_denied", "Administrator role is required.", status=403)
