import { randomUUID } from "node:crypto";
import { DebugRevealContractError, parseDebugRevealActivationInput, parseDebugRevealRevokeInput, } from "./debugRevealContract.js";
export class DebugRevealServiceError extends Error {
    code;
    statusCode;
    status;
    constructor(code, message, statusCode, status = undefined) {
        super(message);
        this.code = code;
        this.statusCode = statusCode;
        this.status = status;
        this.name = "DebugRevealServiceError";
    }
}
export class DebugRevealService {
    store;
    audit;
    now;
    constructor(store, audit, now = () => new Date()) {
        this.store = store;
        this.audit = audit;
        this.now = now;
    }
    status(workspaceId) {
        return this.expireIfNeeded(workspaceId);
    }
    activate(input) {
        const parsed = parseDebugRevealActivationInput(input.body);
        const existing = this.expireIfNeeded(input.workspaceId);
        if (existing.active) {
            throw new DebugRevealServiceError("active_session_conflict", "A debug reveal session is already active for this workspace.", 409, existing);
        }
        const activatedAt = this.now();
        const expiresAt = new Date(activatedAt.getTime() + parsed.ttlSeconds * 1_000).toISOString();
        const revealSessionId = `drevs_${randomUUID()}`;
        this.store.appendDebugRevealEvent({
            id: createDebugRevealEventId(),
            workspaceId: input.workspaceId,
            revealSessionId,
            kind: "activated",
            eventAt: activatedAt.toISOString(),
            expiresAt,
            ttlSeconds: parsed.ttlSeconds,
            surfaces: parsed.surfaces,
            ...(parsed.reasonLabel ? { reasonLabel: parsed.reasonLabel } : {}),
        });
        this.audit.append({
            type: "debug_reveal.activated",
            severity: "warning",
            message: "Debug reveal break-glass session activated.",
            metadata: {
                workspaceId: input.workspaceId,
                reveal: { id: revealSessionId, state: "active" },
                surfaces: parsed.surfaces,
                expiresAt,
                ttlSeconds: parsed.ttlSeconds,
                ...(parsed.reasonLabel ? { reasonLabel: parsed.reasonLabel } : {}),
                authority: {
                    localUiSession: true,
                    requiredScope: "debug:reveal",
                    granted: input.actor.scopes.includes("debug:reveal"),
                },
                localOnly: true,
                cloudSync: "disabled",
                rawStorage: "disabled",
            },
        });
        return this.store.getDebugRevealStatus({
            workspaceId: input.workspaceId,
            now: this.now(),
        });
    }
    revoke(input) {
        const parsed = parseDebugRevealRevokeInput(input.body ?? {});
        const existing = this.expireIfNeeded(input.workspaceId);
        if (!existing.active || existing.current?.id !== input.revealSessionId) {
            throw new DebugRevealServiceError("session_not_found", "Active debug reveal session was not found for this workspace.", 404, existing);
        }
        const reasonLabel = parsed.reasonLabel ?? existing.current.reasonLabel;
        this.store.appendDebugRevealEvent({
            id: createDebugRevealEventId(),
            workspaceId: input.workspaceId,
            revealSessionId: input.revealSessionId,
            kind: "revoked",
            eventAt: this.now().toISOString(),
            expiresAt: existing.current.expiresAt,
            ttlSeconds: existing.current.ttlSeconds,
            surfaces: existing.current.surfaces,
            ...(reasonLabel ? { reasonLabel } : {}),
        });
        this.audit.append({
            type: "debug_reveal.revoked",
            severity: "info",
            message: "Debug reveal break-glass session revoked.",
            metadata: {
                workspaceId: input.workspaceId,
                reveal: { id: input.revealSessionId, state: "revoked" },
                surfaces: existing.current.surfaces,
                ...(parsed.reasonLabel ? { reasonLabel: parsed.reasonLabel } : {}),
                authority: {
                    localUiSession: true,
                    requiredScope: "debug:reveal",
                    granted: input.actor.scopes.includes("debug:reveal"),
                },
                localOnly: true,
                cloudSync: "disabled",
                rawStorage: "disabled",
            },
        });
        return this.store.getDebugRevealStatus({
            workspaceId: input.workspaceId,
            now: this.now(),
        });
    }
    expireIfNeeded(workspaceId) {
        const now = this.now();
        const status = this.store.getDebugRevealStatus({ workspaceId, now });
        if (status.state !== "expired" ||
            !status.current ||
            status.lastEvent?.kind !== "activated") {
            return status;
        }
        this.store.appendDebugRevealEvent({
            id: createDebugRevealEventId(),
            workspaceId,
            revealSessionId: status.current.id,
            kind: "expired",
            eventAt: now.toISOString(),
            expiresAt: status.current.expiresAt,
            ttlSeconds: status.current.ttlSeconds,
            surfaces: status.current.surfaces,
            ...(status.current.reasonLabel
                ? { reasonLabel: status.current.reasonLabel }
                : {}),
        });
        this.audit.append({
            type: "debug_reveal.expired",
            severity: "info",
            message: "Debug reveal break-glass session expired.",
            metadata: {
                workspaceId,
                reveal: { id: status.current.id, state: "expired" },
                surfaces: status.current.surfaces,
                expiresAt: status.current.expiresAt,
                localOnly: true,
                cloudSync: "disabled",
                rawStorage: "disabled",
            },
        });
        return this.store.getDebugRevealStatus({ workspaceId, now });
    }
}
export function debugRevealErrorResponse(error) {
    if (error instanceof DebugRevealContractError) {
        return {
            statusCode: error.statusCode,
            body: {
                error: error.code,
                message: error.message,
                ...error.details,
            },
        };
    }
    if (error instanceof DebugRevealServiceError) {
        return {
            statusCode: error.statusCode,
            body: {
                error: error.code,
                message: error.message,
                ...(error.status
                    ? { debugReveal: formatDebugRevealStatus(error.status) }
                    : {}),
            },
        };
    }
    return undefined;
}
export function formatDebugRevealStatus(status) {
    return {
        state: status.state,
        active: status.active,
        safeguards: {
            localOnly: true,
            cloudSync: status.cloudSync,
            rawStorage: status.rawStorage,
        },
        audit: {
            markerCount: status.markerCount,
            ...(status.lastEvent
                ? {
                    lastMarker: {
                        id: status.lastEvent.id,
                        kind: status.lastEvent.kind,
                        at: status.lastEvent.eventAt,
                    },
                }
                : {}),
        },
        ...(status.current
            ? {
                session: {
                    id: status.current.id,
                    activatedAt: status.current.activatedAt,
                    expiresAt: status.current.expiresAt,
                    ttlSeconds: status.current.ttlSeconds,
                    ttlRemainingSeconds: status.current.ttlRemainingSeconds,
                    surfaces: status.current.surfaces,
                    ...(status.current.reasonLabel
                        ? { reasonLabel: status.current.reasonLabel }
                        : {}),
                },
            }
            : {}),
    };
}
function createDebugRevealEventId() {
    return `dreve_${process.hrtime.bigint().toString(36)}_${randomUUID()}`;
}
