import type {
    DevToolActionDto,
    DevToolDatabaseSummaryDto,
    DevToolDeploySummaryDto,
    DevToolPageDto,
    DevToolProcessDto,
    DevToolTaskDto,
    DevToolTaskStatusDto,
    DevToolsOverviewDto,
} from "../dto/dev-tools.dto.js"

export const DevToolsWsEvent = {
    EVENT: "dev-tools:event",
    JOIN: "join-dev-tools",
    LEAVE: "leave-dev-tools",
    KEEPALIVE: "keepalive-dev-tools",
    ROOM: "dev-tools",
} as const

export type DevToolsRealtimeScope = "global" | "local" | "current" | "release"

export interface DevToolsJoinPayload {
    page?: DevToolPageDto | "all"
    contextKey?: string | null
}

export interface NormalizedDevToolsJoinPayload {
    page: DevToolPageDto | "all"
    contextKey: string | null
}

export interface ResolveDevToolsRealtimeContextKeyInput {
    scope: DevToolsRealtimeScope
    releaseName?: string | null
}

export interface ParsedDevToolsRealtimeContextKey {
    contextKey: string
    scope: DevToolsRealtimeScope
    releaseName: string | null
    runtimeScope: "current" | "release" | null
}

function normalizeReleaseName(value: string | null | undefined) {
    const normalizedValue = value?.trim() || ""
    return normalizedValue || null
}

export function normalizeDevToolsRealtimeContextKey(value: string | null | undefined) {
    const normalizedValue = value?.trim() || ""
    if (!normalizedValue) {
        return null
    }

    if (normalizedValue === "global" || normalizedValue === "local" || normalizedValue === "current") {
        return normalizedValue
    }

    const separatorIndex = normalizedValue.indexOf(":")
    if (separatorIndex <= 0 || separatorIndex >= normalizedValue.length - 1) {
        return null
    }

    const scope = normalizedValue.slice(0, separatorIndex).toLowerCase()
    const releaseName = normalizeReleaseName(normalizedValue.slice(separatorIndex + 1))
    if (!releaseName || (scope !== "global" && scope !== "release")) {
        return null
    }

    return `${scope}:${releaseName}`
}

export function resolveDevToolsRealtimeContextKey(input: ResolveDevToolsRealtimeContextKeyInput) {
    const releaseName = normalizeReleaseName(input.releaseName)
    switch (input.scope) {
        case "local":
            return "local"
        case "current":
            return "current"
        case "release":
            return releaseName ? `release:${releaseName}` : "current"
        case "global":
        default:
            return releaseName ? `global:${releaseName}` : "global"
    }
}

export function parseDevToolsRealtimeContextKey(value: string | null | undefined): ParsedDevToolsRealtimeContextKey | null {
    const normalizedValue = normalizeDevToolsRealtimeContextKey(value)
    if (!normalizedValue) {
        return null
    }

    if (normalizedValue === "global") {
        return {
            contextKey: normalizedValue,
            scope: "global",
            releaseName: null,
            runtimeScope: null,
        }
    }

    if (normalizedValue === "local") {
        return {
            contextKey: normalizedValue,
            scope: "local",
            releaseName: null,
            runtimeScope: null,
        }
    }

    if (normalizedValue === "current") {
        return {
            contextKey: normalizedValue,
            scope: "current",
            releaseName: null,
            runtimeScope: "current",
        }
    }

    const separatorIndex = normalizedValue.indexOf(":")
    const scope = normalizedValue.slice(0, separatorIndex) as "global" | "release"
    const releaseName = normalizedValue.slice(separatorIndex + 1)

    return {
        contextKey: normalizedValue,
        scope,
        releaseName,
        runtimeScope: scope === "release" ? "release" : null,
    }
}

export function matchDevToolsRealtimeContextKey(expected: string | null | undefined, actual: string | null | undefined) {
    const normalizedExpected = normalizeDevToolsRealtimeContextKey(expected)
    const normalizedActual = normalizeDevToolsRealtimeContextKey(actual)
    return Boolean(normalizedExpected && normalizedActual && normalizedExpected === normalizedActual)
}

export function normalizeDevToolsJoinPayload(input?: DevToolsJoinPayload | null): NormalizedDevToolsJoinPayload {
    return {
        page: input?.page ?? "all",
        contextKey: normalizeDevToolsRealtimeContextKey(input?.contextKey),
    }
}

export function buildDevToolsRealtimeSubscriptionKey(input?: DevToolsJoinPayload | null) {
    const normalizedInput = normalizeDevToolsJoinPayload(input)
    return `${normalizedInput.page}::${normalizedInput.contextKey ?? ""}`
}

export function buildDevToolsRealtimePageRoom(page: DevToolPageDto | "all") {
    return `${DevToolsWsEvent.ROOM}:page:${page}`
}

export function buildDevToolsRealtimeContextRoom(page: DevToolPageDto | "all", contextKey: string | null | undefined) {
    const normalizedContextKey = normalizeDevToolsRealtimeContextKey(contextKey)
    if (!normalizedContextKey) {
        return null
    }

    return `${DevToolsWsEvent.ROOM}:page:${page}:context:${normalizedContextKey}`
}

export function buildDevToolsRealtimeAllContextRoom(contextKey: string | null | undefined) {
    return buildDevToolsRealtimeContextRoom("all", contextKey)
}

export function resolveDevToolsRealtimeSubscriptionRooms(input?: DevToolsJoinPayload | null) {
    const normalizedInput = normalizeDevToolsJoinPayload(input)
    const rooms = [buildDevToolsRealtimePageRoom(normalizedInput.page)]
    const contextualPageRoom = buildDevToolsRealtimeContextRoom(normalizedInput.page, normalizedInput.contextKey)
    if (contextualPageRoom) {
        rooms.push(contextualPageRoom)
        const allContextRoom = buildDevToolsRealtimeAllContextRoom(normalizedInput.contextKey)
        if (allContextRoom && normalizedInput.page !== "all") {
            rooms.push(allContextRoom)
        }
    }

    return [...new Set(rooms)]
}

export type DevToolsRealtimeKind = "page-snapshot" | "task-updated" | "summary-updated" | "runtime-updated" | "log-chunk" | "terminal-output" | "terminal-exit"

export interface DevToolsPageSnapshotPayload {
    page: DevToolPageDto | "all"
    kind: "page-snapshot"
    occurredAt: string
    contextKey?: string | null
    overview: DevToolsOverviewDto
}

export interface DevToolsTaskUpdatedPayload {
    page: "all"
    kind: "task-updated"
    occurredAt: string
    contextKey?: string | null
    task: DevToolTaskDto
}

export interface DevToolsSummaryUpdatedPayload {
    page: "all"
    kind: "summary-updated"
    occurredAt: string
    deploySummary?: DevToolDeploySummaryDto | null
    databaseSummary?: DevToolDatabaseSummaryDto | null
}

export interface DevToolsRuntimeUpdatedPayload {
    page: "runtime"
    kind: "runtime-updated"
    occurredAt: string
    contextKey?: string | null
    taskId: string | null
    taskActionId: string | null
    taskStatus: DevToolTaskStatusDto | null
    processKeys: string[]
    logProcessKeys: string[]
    actions?: DevToolActionDto[]
    tasks?: DevToolTaskDto[]
    processes?: DevToolProcessDto[]
}

export interface DevToolsLogChunkPayload {
    page: "runtime"
    kind: "log-chunk"
    occurredAt: string
    contextKey?: string | null
    processKey: string
    content: string
    cursor: number
    resetRequired: boolean
    truncated: boolean
}

export interface DevToolsTerminalOutputPayload {
    page: "runtime"
    kind: "terminal-output"
    occurredAt: string
    sessionId: string
    processKey: string
    chunk: string
}

export interface DevToolsTerminalExitPayload {
    page: "runtime"
    kind: "terminal-exit"
    occurredAt: string
    sessionId: string
    processKey: string
    exitCode: number | null
}

export type DevToolsWsPayload =
    | DevToolsPageSnapshotPayload
    | DevToolsTaskUpdatedPayload
    | DevToolsSummaryUpdatedPayload
    | DevToolsRuntimeUpdatedPayload
    | DevToolsLogChunkPayload
    | DevToolsTerminalOutputPayload
    | DevToolsTerminalExitPayload

export function resolveDevToolsRealtimePayloadRooms(payload: DevToolsWsPayload) {
    if (payload.kind === "task-updated" || payload.kind === "summary-updated") {
        if (payload.kind === "task-updated") {
            const contextualRoom = buildDevToolsRealtimeAllContextRoom(payload.contextKey ?? payload.task.contextKey)
            if (contextualRoom) {
                return [contextualRoom]
            }
        }

        return [DevToolsWsEvent.ROOM]
    }

    if (payload.kind === "log-chunk") {
        const contextualRoom = buildDevToolsRealtimeContextRoom(payload.page, payload.contextKey)
        if (contextualRoom) {
            return [contextualRoom]
        }

        return [buildDevToolsRealtimePageRoom(payload.page)]
    }

    if (payload.kind === "terminal-output" || payload.kind === "terminal-exit") {
        return [buildDevToolsRealtimePageRoom(payload.page)]
    }

    if (payload.kind === "page-snapshot") {
        const contextualRoom = buildDevToolsRealtimeContextRoom(payload.page, payload.contextKey)
        if (contextualRoom) {
            return [
                payload.page === "all"
                    ? buildDevToolsRealtimeAllContextRoom(payload.contextKey) ?? contextualRoom
                    : contextualRoom,
            ]
        }

        return [buildDevToolsRealtimePageRoom(payload.page)]
    }

    const runtimeContextRoom = buildDevToolsRealtimeContextRoom(payload.page, payload.contextKey)
    if (runtimeContextRoom) {
        return [runtimeContextRoom]
    }

    return [buildDevToolsRealtimePageRoom(payload.page)]
}

export type DevToolsWsEvents = {
    [DevToolsWsEvent.EVENT]: DevToolsWsPayload
}
