import { credentialScopeVersionFromStatus, openAiRuntimeCredentialScopeFingerprint, } from "./credentialScope.js";
import { classifyRuntimeFailureDiagnostic, sanitizeOpenCodeBinaryStatus, sanitizeRuntimeLifecycleDiagnostic, } from "./runtimeLifecycleDiagnostics.js";
import { RuntimeSmokeResultParseError, } from "./RuntimeSmokeResultStore.js";
export class RuntimeDiagnosticsService {
    runtime;
    providerProfileAuth;
    smokeResultStore;
    constructor(runtime, providerProfileAuth, smokeResultStore) {
        this.runtime = runtime;
        this.providerProfileAuth = providerProfileAuth;
        this.smokeResultStore = smokeResultStore;
    }
    async statusForWorkspace(workspaceId) {
        const [runtime, credential, smokeRead] = await Promise.all([
            this.runtime.status(),
            this.providerProfileAuth.statusApiKeyInternal(workspaceId, "openai"),
            this.readLastSmoke(),
        ]);
        const reasons = [];
        const recommendedActions = [];
        const credentialFreshness = evaluateCredentialFreshness(workspaceId, runtime, credential, reasons, recommendedActions);
        if (runtime.state === "blocked_unverified_binary") {
            const binaryDiagnostic = binaryLifecycleDiagnostic(runtime.binary);
            reasons.push({
                code: "opencode_binary_not_verified",
                severity: "critical",
                message: binaryDiagnostic.reason,
            });
            recommendedActions.push({
                code: "configure_verified_opencode_binary",
                label: "Configure verified OpenCode binary",
                description: "Run the trusted OpenCode artifact bootstrap or configure signed metadata before starting runtime sessions.",
            });
        }
        if (runtime.state === "failed") {
            const failureDiagnostic = runtimeFailureDiagnostic(runtime);
            reasons.push({
                code: "opencode_runtime_failed",
                severity: "critical",
                message: failureDiagnostic.reason,
            });
            recommendedActions.push({
                code: "inspect_runtime_failure",
                label: "Inspect runtime failure",
                description: "Check the bridge runtime status and restart the runtime after addressing the recorded failure reason.",
            });
        }
        else if (runtime.state === "starting") {
            reasons.push({
                code: "opencode_runtime_starting",
                severity: "info",
                message: "OpenCode runtime is starting and readiness has not completed yet.",
            });
        }
        if (credential.state !== "available") {
            reasons.push({
                code: "openai_credential_not_configured",
                severity: "warning",
                message: "No workspace-scoped OpenAI API key is currently available in the local credential vault.",
            });
            recommendedActions.push({
                code: "store_openai_api_key",
                label: "Store OpenAI API key locally",
                description: "Add a workspace OpenAI key in Settings so future OpenCode sessions can receive a PatchTrail-managed credential.",
            });
        }
        if (credential.degraded) {
            reasons.push({
                code: "credential_store_degraded",
                severity: "warning",
                message: "The credential store is using a degraded backend. Runtime can continue, but secure storage needs attention.",
            });
        }
        const sanitizedSmoke = smokeRead.result
            ? sanitizeSmokeResult(smokeRead.result)
            : undefined;
        if (smokeRead.warning) {
            reasons.push({
                code: "last_smoke_result_unreadable",
                severity: "warning",
                message: smokeRead.warning,
            });
            recommendedActions.push({
                code: "rerun_opencode_smoke",
                label: "Re-run OpenCode smoke",
                description: "Run pnpm smoke:opencode from agent-bridge to refresh the runtime smoke diagnostic artifact.",
            });
        }
        else if (sanitizedSmoke?.status === "failed") {
            reasons.push({
                code: "last_opencode_smoke_failed",
                severity: "warning",
                message: sanitizedSmoke.error ??
                    sanitizedSmoke.summary ??
                    "The last recorded OpenCode runtime smoke failed.",
            });
            recommendedActions.push({
                code: "rerun_opencode_smoke_after_fix",
                label: "Fix and re-run OpenCode smoke",
                description: "Address the smoke failure and run pnpm smoke:opencode again before trusting runtime behavior.",
            });
        }
        const restartRequired = credentialFreshness === "stale";
        const overallState = chooseOverallState({
            runtimeState: runtime.state,
            credentialFreshness,
            credentialDegraded: Boolean(credential.degraded),
            ...(sanitizedSmoke ? { smoke: sanitizedSmoke } : {}),
            ...(smokeRead.warning ? { smokeWarning: smokeRead.warning } : {}),
        });
        return {
            provider: "opencode",
            workspaceId,
            overallState,
            runtime: sanitizeRuntimeStatus(runtime),
            credential: sanitizeCredentialStatus(credential),
            credentialFreshness,
            restartRequired,
            reasons,
            recommendedActions: dedupeActions(recommendedActions),
            ...(sanitizedSmoke ? { lastSmoke: sanitizedSmoke } : {}),
        };
    }
    async readLastSmoke() {
        if (!this.smokeResultStore)
            return {};
        try {
            const result = await this.smokeResultStore.readLast();
            return result ? { result } : {};
        }
        catch (error) {
            if (error instanceof RuntimeSmokeResultParseError) {
                return { warning: error.message };
            }
            return {
                warning: error instanceof Error
                    ? `Runtime smoke result could not be read: ${error.message}`
                    : "Runtime smoke result could not be read.",
            };
        }
    }
}
function evaluateCredentialFreshness(workspaceId, runtime, credential, reasons, recommendedActions) {
    const processState = runtime.process?.state;
    const activeScope = runtime.process?.credentialScope;
    const runtimeIsCredentialBearing = processState === "running" && activeScope;
    if (credential.state !== "available") {
        if (runtimeIsCredentialBearing) {
            reasons.push({
                code: "runtime_using_revoked_or_missing_credential",
                severity: "critical",
                message: "The runtime is still associated with a credential scope, but the current workspace credential is no longer available.",
            });
            recommendedActions.push(restartRuntimeAction());
            return "stale";
        }
        return "not_configured";
    }
    const expectedFingerprint = openAiRuntimeCredentialScopeFingerprint(workspaceId, credentialScopeVersionFromStatus(credential));
    if (processState !== "running")
        return "current";
    if (!activeScope) {
        reasons.push({
            code: "runtime_missing_current_credential_scope",
            severity: "warning",
            message: "OpenCode is running without the current workspace credential scope. Restart the runtime before sending credential-backed prompts.",
        });
        recommendedActions.push(restartRuntimeAction());
        return "stale";
    }
    if (activeScope.provider !== "openai" ||
        activeScope.kind !== "openai_api_key" ||
        activeScope.workspaceId !== workspaceId) {
        reasons.push({
            code: "runtime_credential_scope_workspace_mismatch",
            severity: "critical",
            message: "OpenCode is running with a credential scope for a different workspace or provider.",
        });
        recommendedActions.push(restartRuntimeAction());
        return "stale";
    }
    if (activeScope.fingerprint !== expectedFingerprint) {
        reasons.push({
            code: "runtime_credential_scope_stale",
            severity: "warning",
            message: "OpenCode is running with an older workspace credential scope. Restart the runtime to use the latest stored key.",
        });
        recommendedActions.push(restartRuntimeAction());
        return "stale";
    }
    return "current";
}
function chooseOverallState(input) {
    if (input.runtimeState === "failed")
        return "failed";
    if (input.runtimeState === "blocked_unverified_binary")
        return "blocked";
    if (input.runtimeState === "starting")
        return "degraded";
    if (input.credentialFreshness === "stale" ||
        input.credentialFreshness === "not_configured" ||
        input.credentialDegraded ||
        input.smoke?.status === "failed" ||
        input.smokeWarning) {
        return "attention_required";
    }
    return "ok";
}
function lifecycleDiagnosticForRuntimeStatus(runtime) {
    if (runtime.state === "blocked_unverified_binary") {
        return binaryLifecycleDiagnostic(runtime.binary);
    }
    if (runtime.state === "failed" || runtime.lastError) {
        return runtimeFailureDiagnostic(runtime);
    }
    return undefined;
}
function binaryLifecycleDiagnostic(binary) {
    return sanitizeRuntimeLifecycleDiagnostic(binary, {
        reasonCode: binary.state === "metadata_unavailable"
            ? "metadata_unavailable"
            : binary.state === "not_configured"
                ? "not_configured"
                : "verification_rejected",
    });
}
function runtimeFailureDiagnostic(runtime) {
    return classifyRuntimeFailureDiagnostic({
        reason: runtime.lastError ?? runtime.process?.lastError,
    });
}
function sanitizeRuntimeStatus(runtime) {
    const lifecycleDiagnostic = lifecycleDiagnosticForRuntimeStatus(runtime);
    return {
        provider: runtime.provider,
        state: runtime.state,
        binary: sanitizeOpenCodeBinaryStatus(runtime.binary),
        ...(runtime.process
            ? { process: sanitizeProcessStatus(runtime.process) }
            : {}),
        activeSessions: runtime.activeSessions,
        ...(runtime.lastError && lifecycleDiagnostic
            ? {
                lastError: lifecycleDiagnostic.reason,
                reasonCode: lifecycleDiagnostic.reasonCode,
                phase: lifecycleDiagnostic.phase,
                timestamp: lifecycleDiagnostic.timestamp,
            }
            : {}),
    };
}
function sanitizeProcessStatus(process) {
    const failureDiagnostic = process.lastError
        ? classifyRuntimeFailureDiagnostic({ reason: process.lastError })
        : undefined;
    return {
        state: process.state,
        host: process.host,
        ...(process.port !== undefined ? { port: process.port } : {}),
        ...(process.startedAt ? { startedAt: process.startedAt } : {}),
        ...(process.stoppedAt ? { stoppedAt: process.stoppedAt } : {}),
        ...(failureDiagnostic
            ? {
                lastError: failureDiagnostic.reason,
                reasonCode: failureDiagnostic.reasonCode,
                phase: failureDiagnostic.phase,
                timestamp: failureDiagnostic.timestamp,
            }
            : {}),
        ...(process.opencodeVersion
            ? { opencodeVersion: process.opencodeVersion }
            : {}),
    };
}
function sanitizeSmokeResult(result) {
    const binaryLabels = sanitizeOpenCodeBinaryStatus({
        state: "verified",
        ...(result.opencodeVersion
            ? { opencodeVersion: result.opencodeVersion }
            : {}),
        ...(result.platform ? { platform: result.platform } : {}),
    });
    const sanitized = {
        status: result.status,
        ranAt: safeSmokeTimestamp(result.ranAt),
        summary: result.status === "passed"
            ? "OpenCode runtime smoke passed."
            : "OpenCode runtime smoke failed.",
        allowExternal: result.allowExternal,
        ...(Number.isFinite(result.durationMs) && result.durationMs !== undefined
            ? { durationMs: result.durationMs }
            : {}),
        ...(binaryLabels.opencodeVersion
            ? { opencodeVersion: binaryLabels.opencodeVersion }
            : {}),
        ...(binaryLabels.platform ? { platform: binaryLabels.platform } : {}),
    };
    if (result.status === "failed" && result.error) {
        sanitized.error = classifyRuntimeFailureDiagnostic({
            reason: result.error,
        }).reason;
    }
    return sanitized;
}
function safeSmokeTimestamp(value) {
    const parsed = new Date(value);
    if (Number.isFinite(parsed.getTime()))
        return parsed.toISOString();
    return new Date(0).toISOString();
}
function sanitizeCredentialStatus(credential) {
    return {
        profileKey: credential.profileKey,
        displayName: credential.displayName,
        credential: { ...credential.credential },
        runtimeExecutable: credential.runtimeExecutable,
        runnable: credential.runnable,
        runnableBlockerReason: credential.runnableBlockerReason,
        state: credential.state,
        ...(credential.maskedLabel ? { maskedLabel: credential.maskedLabel } : {}),
        ...(credential.updatedAt ? { updatedAt: credential.updatedAt } : {}),
        ...(credential.backend ? { backend: credential.backend } : {}),
        ...(credential.degraded !== undefined
            ? { degraded: credential.degraded }
            : {}),
    };
}
function restartRuntimeAction() {
    return {
        code: "restart_opencode_runtime",
        label: "Restart OpenCode runtime",
        description: "Stop and start the local OpenCode runtime so it uses the current workspace credential scope.",
    };
}
function dedupeActions(actions) {
    const seen = new Set();
    return actions.filter((action) => {
        if (seen.has(action.code))
            return false;
        seen.add(action.code);
        return true;
    });
}
