export type RuntimeLevel = "ok" | "warn" | "error";
export type RuntimeComponentKind = "adapter" | "extension" | "transport";
export type StreamRuntimeState = "starting" | "ready" | "degraded" | "stopped";

export type RuntimeComponentStatus = {
    id: string;
    kind: RuntimeComponentKind;
    level: RuntimeLevel;
    message: string;
    updatedAt: string;
    details?: Record<string, unknown>;
};

export type StreamRuntimeStatus = {
    streamId: string;
    state: StreamRuntimeState;
    updatedAt: string;
    components: RuntimeComponentStatus[];
};

export type ConnectedClientStats = {
    total: number;
    admin: number;
    viewer: number;
};

function nowIso(): string {
    return new Date().toISOString();
}

export class RuntimeManager {
    private readonly streams = new Map<
        string,
        {
            state: StreamRuntimeState;
            updatedAt: string;
            components: Map<string, RuntimeComponentStatus>;
        }
    >();

    private connectedClients: ConnectedClientStats = {
        total: 0,
        admin: 0,
        viewer: 0
    };

    public setStreamState(streamId: string, state: StreamRuntimeState, reason?: string): void {
        const entry = this.ensureStream(streamId);
        entry.state = state;
        entry.updatedAt = nowIso();
        if (reason) {
            const existing = entry.components.get("runtime-state");
            entry.components.set("runtime-state", {
                id: "runtime-state",
                kind: "transport",
                level: state === "degraded" ? "warn" : "ok",
                message: reason,
                updatedAt: entry.updatedAt,
                details: existing?.details
            });
        }
    }

    public setComponentStatus(streamId: string, status: RuntimeComponentStatus): void {
        const entry = this.ensureStream(streamId);
        const updatedAt = status.updatedAt || nowIso();
        entry.components.set(`${status.kind}:${status.id}`, {
            ...status,
            updatedAt
        });

        if (status.level === "error" || status.level === "warn") {
            entry.state = "degraded";
            entry.updatedAt = updatedAt;
        } else if (entry.state === "starting") {
            entry.state = "ready";
            entry.updatedAt = updatedAt;
        }
    }

    public getStreamRuntime(streamId: string): StreamRuntimeStatus | null {
        const entry = this.streams.get(streamId);
        if (!entry) {
            return null;
        }
        return {
            streamId,
            state: entry.state,
            updatedAt: entry.updatedAt,
            components: Array.from(entry.components.values()).sort((a, b) =>
                a.id.localeCompare(b.id)
            )
        };
    }

    public listRuntime(): StreamRuntimeStatus[] {
        return Array.from(this.streams.entries())
            .map(([streamId]) => this.getStreamRuntime(streamId))
            .filter((item): item is StreamRuntimeStatus => item !== null)
            .sort((a, b) => a.streamId.localeCompare(b.streamId));
    }

    public setConnectedClients(stats: ConnectedClientStats): void {
        this.connectedClients = { ...stats };
    }

    public getConnectedClients(): ConnectedClientStats {
        return { ...this.connectedClients };
    }

    private ensureStream(streamId: string): {
        state: StreamRuntimeState;
        updatedAt: string;
        components: Map<string, RuntimeComponentStatus>;
    } {
        const existing = this.streams.get(streamId);
        if (existing) {
            return existing;
        }

        const created = {
            state: "starting" as StreamRuntimeState,
            updatedAt: nowIso(),
            components: new Map<string, RuntimeComponentStatus>()
        };
        this.streams.set(streamId, created);
        return created;
    }
}
