from __future__ import annotations

import json
import logging
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Literal, TypeAlias

from pydantic import BaseModel, Field, PrivateAttr

from gateway_core.domain.node.NodeInfo import NodeInfo, NodeEndpoint, NodeType
from gateway_runtime.RuntimeErrors import RuntimeStartupError

logger = logging.getLogger(__name__)

IntegrationType: TypeAlias = Literal["deltagrid"]


# -------------------------------------------------
# RuntimeConfig
# - single config object used by runtime
# - parses gateway.json (IO) and exposes runtime DTOs
# -------------------------------------------------

class MqttConfig(BaseModel):
    host: str
    port: int = 1883
    rpcTopic: str
    tls: bool = False


class IntegrationConfigModel(BaseModel):
    type: IntegrationType
    name: str | None = None
    mqtt: MqttConfig | None = None


class HttpEndpointConfig(BaseModel):
    type: Literal["http"]
    baseUrl: str


class NodeConfigModel(BaseModel):
    nodeId: str
    tags: list[str] = Field(default_factory=list)
    endpoint: HttpEndpointConfig


@dataclass(frozen=True, slots=True)
class IntegrationConfig:
    type: IntegrationType
    name: str | None
    mqtt: dict[str, Any]


class RuntimeConfig(BaseModel):
    integrations: list[IntegrationConfigModel] = Field(default_factory=list)
    nodes: list[NodeConfigModel] = Field(default_factory=list)

    _integrationDtos: list["IntegrationConfig"] | None = PrivateAttr(default=None)
    _nodeInfos: list[NodeInfo] | None = PrivateAttr(default=None)

    def applyDefaults(self) -> "RuntimeConfig":
        if not self.nodes:
            baseUrl = os.getenv("NODE_AGENT_BASE_URL", "http://127.0.0.1:1898")
            self.nodes = [
                NodeConfigModel(
                    nodeId="local-node-agent",
                    tags=["local"],
                    endpoint=HttpEndpointConfig(type="http", baseUrl=baseUrl),
                )
            ]
        return self

    def validateConfig(self) -> "RuntimeConfig":
        if not self.integrations:
            raise RuntimeError("gateway.json.integrations is empty")
        return self

    @property
    def integrationConfigs(self) -> list["IntegrationConfig"]:
        if self._integrationDtos is None:
            self._integrationDtos = [
                IntegrationConfig(
                    type=i.type,
                    name=i.name,
                    mqtt=i.mqtt.dict() if i.mqtt else {},
                )
                for i in self.integrations
            ]
        return self._integrationDtos

    @property
    def nodeInfos(self) -> list[NodeInfo]:
        if self._nodeInfos is None:
            self._nodeInfos = [
                NodeInfo(
                    nodeId=n.nodeId,
                    tags=tuple(n.tags),
                    endpoint=NodeEndpoint(
                        type=NodeType.HTTP,
                        baseUrl=str(n.endpoint.baseUrl),
                    ),
                )
                for n in self.nodes
            ]
        return self._nodeInfos

# -------------------------------------------------
# IO helpers
# -------------------------------------------------

def loadRuntimeConfig() -> RuntimeConfig:
    workdir = os.getenv("ECP_WORK_FILES_DIR")
    if not workdir:
        raise RuntimeStartupError("ECP_WORK_FILES_DIR is not set")

    path = Path(workdir) / "config" / "gateway.json"
    if not path.exists():
        raise RuntimeStartupError(f"gateway.json not found: {path}")

    raw = path.read_text(encoding="utf-8").strip()
    if not raw:
        raise RuntimeStartupError(f"gateway.json is empty: {path}")

    try:
        return (
            RuntimeConfig.parse_obj(json.loads(raw))
            .applyDefaults()
            .validateConfig()
        )
    except Exception as e:
        logger.exception("Invalid gateway.json: %s", path)
        raise RuntimeStartupError(f"Invalid gateway.json: {path}") from e
