import { spawn } from 'node:child_process';
import { randomBytes } from 'node:crypto';
import { createServer } from 'node:http';
import { createConnection } from 'node:net';
import * as os from 'node:os';
import * as fs from 'node:fs';
import * as path from 'node:path';
import {
  invokeHostControlFromStatus,
  type IHostSupervisorHostStatus,
} from '@open-matrix/host-control';

import {
  readRegistryCredential,
  removeRegistryCredential,
  saveRegistryCredential,
  type IRegistryCredential,
} from '../utils/auth-store.js';
import {
  readHiveCastHostLink,
  readHiveCastLocalOwnerTransport,
  readHiveCastLinkStatus,
  ensureHiveCastInstallIdentity,
  removeHiveCastCredentialFiles,
  removeHiveCastHostLink,
  saveHiveCastHostLink,
  type IHiveCastLinkStatus,
  type IHiveCastHostLinkRecord,
  type IHiveCastNatsConfig,
  type IHiveCastNatsCredentials,
} from '../utils/hivecast-link-store.js';

export interface IAuthContextOptions {
  readonly cwd?: string;
  readonly registry?: string;
  readonly credentialsPath?: string;
  readonly home?: string;
}

export interface ILoginCommandOptions extends IAuthContextOptions {
  readonly username: string;
  readonly token: string;
  readonly email?: string;
  readonly expiresAt?: string;
}

export interface ILoginCommandResult {
  readonly registry: string;
  readonly username: string;
  readonly credentialsPath: string;
}

export interface ILogoutCommandResult {
  readonly registry: string;
  readonly removed: boolean;
  readonly credentialsPath: string;
}

export interface IWhoamiCommandResult {
  readonly registry: string;
  readonly authenticated: boolean;
  readonly username?: string;
  readonly email?: string;
  readonly expiresAt?: string;
}

export interface IHiveCastLoginCommandOptions extends IAuthContextOptions {
  readonly cloudUrl?: string;
  readonly setupCode?: string;
  readonly routeKey?: string;
  readonly device?: boolean;
  readonly hostName?: string;
  readonly callbackUrl?: string;
  readonly pairNonce?: string;
  readonly localPort?: number;
  readonly waitForCallback?: boolean;
  readonly noOpen?: boolean;
  readonly forceRelink?: boolean;
  readonly callbackTimeoutMs?: number;
  readonly pollTimeoutMs?: number;
  readonly openApprovalUrl?: (url: string) => void;
  readonly onSetupUrl?: (setupUrl: string) => void;
  readonly onDeviceStart?: (challenge: IHiveCastDeviceChallenge) => void;
  readonly onProgress?: (event: IHiveCastLoginProgressEvent) => void;
}

export interface IHiveCastLoginProgressEvent {
  readonly phase: 'link' | 'namespace' | 'ready';
  readonly message: string;
}

export interface IHiveCastLoginCommandResult {
  readonly mode: 'approval-required' | 'linked';
  readonly linked: boolean;
  readonly alreadyLinked?: boolean;
  readonly cloudUrl: string;
  readonly setupUrl?: string;
  readonly deviceCode?: string;
  readonly userCode?: string;
  readonly verificationUri?: string;
  readonly verificationUriComplete?: string;
  readonly expiresIn?: number;
  readonly matrixHome?: string;
  readonly linkPath?: string;
  readonly hostConfigPath?: string;
  readonly natsCredentialsPath?: string;
  readonly hostLinkId?: string;
  readonly hostId?: string;
  readonly authorityRoot?: string;
  readonly routeKey?: string;
  readonly publicNamespace?: string;
  readonly spaceId?: string;
  readonly hostName?: string;
  readonly deviceSlug?: string;
  readonly authorityCoordinator?: boolean;
  readonly linkedAt?: string;
  readonly hostConfigReload?: IHiveCastHostConfigReloadResult;
}

export interface IHiveCastLogoutCommandResult {
  readonly removed: boolean;
  readonly cloudRevoke?: IHiveCastCloudRevokeResult;
  readonly hostConfigReset: boolean;
  readonly matrixHome: string;
  readonly linkPath: string;
  readonly natsCredentialsPath: string;
  readonly hostConfigReload?: IHiveCastHostConfigReloadResult;
}

export interface IHiveCastRefreshCredentialsCommandResult {
  readonly refreshed: boolean;
  readonly matrixHome: string;
  readonly linkPath: string;
  readonly natsCredentialsPath: string;
  readonly currentCredentialsExpiresAt?: string;
  readonly currentCredentialsSecondsRemaining?: number;
  readonly refreshThresholdSeconds?: number;
  readonly hostLinkId?: string;
  readonly hostId?: string;
  readonly authorityRoot?: string;
  readonly routeKey?: string;
  readonly publicNamespace?: string;
  readonly spaceId?: string;
  readonly hostName?: string;
  readonly deviceSlug?: string;
  readonly authorityCoordinator?: boolean;
  readonly hostConfigReload?: IHiveCastHostConfigReloadResult;
}

export interface IHiveCastWhoamiCommandResult {
  readonly linked: boolean;
  readonly matrixHome: string;
  readonly cloudUrl?: string;
  readonly hostLinkId?: string;
  readonly authorityRoot?: string;
  readonly routeKey?: string;
  readonly publicNamespace?: string;
  readonly spaceId?: string;
  readonly principalId?: string;
  readonly hostName?: string;
  readonly deviceSlug?: string;
  readonly linkedAt?: string;
}

export interface IHiveCastHostConfigReloadResult {
  readonly attempted: boolean;
  readonly applied: boolean;
  readonly statusPath: string;
  readonly error?: string;
}

interface ITokenExchangeResponse {
  readonly ok?: unknown;
  readonly root?: unknown;
  readonly authorityRoot?: unknown;
  readonly deviceRegistryRoot?: unknown;
  readonly routeKey?: unknown;
  readonly publicNamespace?: unknown;
  readonly spaceId?: unknown;
  readonly principalId?: unknown;
  readonly hostName?: unknown;
  readonly deviceSlug?: unknown;
  readonly authorityCoordinator?: unknown;
  readonly hostLinkToken?: unknown;
  readonly hostLinkId?: unknown;
  readonly hostId?: unknown;
  readonly hostLink?: unknown;
  readonly device?: unknown;
  readonly nats?: unknown;
  readonly credentials?: unknown;
  readonly error?: unknown;
}

export interface IHiveCastDeviceChallenge {
  readonly deviceCode: string;
  readonly userCode: string;
  readonly verificationUri: string;
  readonly verificationUriComplete?: string;
  readonly expiresIn: number;
  readonly interval: number;
}

interface IDeviceStartResponse {
  readonly ok?: unknown;
  readonly deviceCode?: unknown;
  readonly userCode?: unknown;
  readonly verificationUri?: unknown;
  readonly verificationUriComplete?: unknown;
  readonly expiresIn?: unknown;
  readonly interval?: unknown;
  readonly error?: unknown;
}

interface IDevicePollResponse {
  readonly ok?: unknown;
  readonly status?: unknown;
  readonly interval?: unknown;
  readonly error?: unknown;
}

interface IPairStartResponse {
  readonly ok?: unknown;
  readonly pairRequestId?: unknown;
  readonly approvalUrl?: unknown;
  readonly expiresIn?: unknown;
  readonly error?: unknown;
}

interface INormalizedSetupExchange {
  readonly authorityRoot: string;
  readonly deviceRegistryRoot?: string;
  readonly hostLinkId?: string;
  readonly hostId?: string;
  readonly routeKey?: string;
  readonly publicNamespace?: string;
  readonly spaceId?: string;
  readonly principalId?: string;
  readonly hostName?: string;
  readonly deviceSlug?: string;
  readonly authorityCoordinator?: boolean;
  readonly hostLinkToken?: string;
  readonly nats: IHiveCastNatsConfig;
  readonly credentials: IHiveCastNatsCredentials;
}

interface IHiveCastCloudRevokeResult {
  readonly attempted: boolean;
  readonly revoked: boolean;
  readonly skippedReason?: string;
  readonly error?: string;
}

export interface IHiveCastLogoutCommandOptions extends IAuthContextOptions {
  readonly localOnly?: boolean;
  readonly revokeCloudLink?: boolean;
  readonly all?: boolean;
}

export interface IHiveCastRefreshCredentialsCommandOptions extends IAuthContextOptions {
  readonly ifExpiringWithinSeconds?: number;
  readonly authorityCoordinator?: boolean;
}

interface IHostConfigReloadExpectation {
  readonly root: string;
  readonly authorityRoot?: string;
  readonly routeKey?: string | null;
  readonly publicNamespace?: string | null;
  readonly spaceId?: string | null;
  readonly forceReload?: boolean;
  readonly label?: 'linked' | 'local-owner';
}

function hostConfigExpectationForLink(
  matrixHome: string,
  record: IHiveCastHostLinkRecord,
  forceReload: boolean,
): IHostConfigReloadExpectation {
  return {
    root: record.authorityRoot,
    authorityRoot: record.authorityRoot,
    routeKey: record.routeKey ?? null,
    publicNamespace: record.publicNamespace ?? null,
    spaceId: record.spaceId ?? null,
    forceReload,
    label: 'linked',
  };
}

function reportLoginProgress(
  options: IHiveCastLoginCommandOptions,
  phase: IHiveCastLoginProgressEvent['phase'],
  message: string,
): void {
  options.onProgress?.({ phase, message });
}

function assertRunningHostConfigReloadApplied(
  result: IHiveCastHostConfigReloadResult,
  expectation: IHostConfigReloadExpectation,
): void {
  if (!result.attempted || result.applied) {
    return;
  }
  const expected = [
    `expectedRoot=${expectation.root}`,
    ...(expectation.authorityRoot ? [`expectedAuthorityRoot=${expectation.authorityRoot}`] : []),
    `statusPath=${result.statusPath}`,
  ].join(' ');
  throw new Error(
    `HIVECAST_LINK_HOST_CONFIG_RELOAD_FAILED: running Host did not apply ${expectation.label ?? 'linked'} config`
    + `${result.error ? `: ${result.error}` : ''}; ${expected}`,
  );
}

function localOwnerTransportForExpectation(
  matrixHome: string,
): { readonly root: string; readonly authorityRoot?: string } | null {
  const localOwnerTransport = readHiveCastLocalOwnerTransport(process.cwd(), matrixHome);
  if (localOwnerTransport) {
    return localOwnerTransport;
  }
  const hostConfig = readJsonRecordFile(path.join(matrixHome, 'host.json'));
  const transport = asRecord(hostConfig?.transport);
  const root = readOptionalString(transport?.root);
  if (!root) {
    return null;
  }
  const authorityRoot = readOptionalString(transport?.authorityRoot);
  return {
    root,
    ...(authorityRoot ? { authorityRoot } : {}),
  };
}

const DEFAULT_HIVECAST_CLOUD_URL = 'https://hivecast.ai';
const DEFAULT_NATS_MAX_PAYLOAD_BYTES = 16 * 1024 * 1024;
const HOST_CONFIG_STATUS_APPLY_TIMEOUT_MS = 60_000;
const HOST_CONFIG_RUNTIME_APPLY_MIN_TIMEOUT_MS = 90_000;
const HOST_CONFIG_RUNTIME_APPLY_PER_RUNTIME_MS = 20_000;
const LOCAL_OWNER_NATS_START_TIMEOUT_MS = 10_000;

function readOptionalString(value: unknown): string | undefined {
  return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
}

function asRecord(value: unknown): Record<string, unknown> | null {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return null;
  }
  return value as Record<string, unknown>;
}

function decodeJwtPayload(token: string): Record<string, unknown> | null {
  const parts = token.split('.');
  if (parts.length !== 3 || !parts[1]) {
    return null;
  }
  try {
    return asRecord(JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')));
  } catch {
    return null;
  }
}

function readJwtExpirationSeconds(token: string): number | null {
  const payload = decodeJwtPayload(token);
  const exp = payload?.exp;
  return typeof exp === 'number' && Number.isFinite(exp) ? exp : null;
}

function readCredentialsExpirationSeconds(filePath: string): number | null {
  if (!fs.existsSync(filePath)) {
    return null;
  }
  let parsed: Record<string, unknown> | null;
  try {
    parsed = asRecord(JSON.parse(fs.readFileSync(filePath, 'utf8')));
  } catch {
    return null;
  }
  const jwt = readOptionalString(parsed?.jwt);
  return jwt ? readJwtExpirationSeconds(jwt) : null;
}

export function normalizeNonNegativeInteger(value: string | undefined, label: string): number | undefined {
  if (value === undefined) {
    return undefined;
  }
  if (!/^\d+$/.test(value.trim())) {
    throw new Error(`${label} must be a non-negative integer number of seconds`);
  }
  return Number.parseInt(value, 10);
}

async function readJsonResponse<T>(
  response: Response,
  label: string,
): Promise<T> {
  const text = await response.text();
  if (!text.trim()) {
    throw new Error(`${label}: empty response body (HTTP ${response.status})`);
  }
  try {
    return JSON.parse(text) as T;
  } catch (error: unknown) {
    const preview = text.slice(0, 240).replace(/\s+/g, ' ').trim();
    const reason = error instanceof Error ? error.message : String(error);
    throw new Error(`${label}: invalid JSON response (HTTP ${response.status}): ${reason}${preview ? `: ${preview}` : ''}`);
  }
}

function normalizeCloudUrl(raw: string | undefined): string {
  const value = raw?.trim() || process.env.HIVECAST_PLATFORM_URL || DEFAULT_HIVECAST_CLOUD_URL;
  const parsed = new URL(value);
  if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
    throw new Error(`HIVECAST_LOGIN_INVALID_CLOUD_URL: unsupported protocol ${parsed.protocol}`);
  }
  return parsed.origin;
}

function openBrowser(url: string): void {
  const command = process.platform === 'darwin'
    ? 'open'
    : process.platform === 'win32'
      ? 'cmd'
      : 'xdg-open';
  const args = process.platform === 'win32'
    ? ['/c', 'start', '', url]
    : [url];
  const child = spawn(command, args, {
    stdio: 'ignore',
    detached: true,
    windowsHide: true,
  });
  child.on('error', () => {
    // Best effort: the setup URL is printed by the caller.
  });
  child.unref();
}

function readJsonRecordFile(filePath: string): Record<string, unknown> | null {
  if (!fs.existsSync(filePath)) {
    return null;
  }
  try {
    return asRecord(JSON.parse(fs.readFileSync(filePath, 'utf8')));
  } catch {
    return null;
  }
}

function hostStatusPath(matrixHome: string): string {
  return path.join(matrixHome, 'host.status.json');
}

function hostRuntimeRecordsDir(matrixHome: string): string {
  return path.join(matrixHome, 'runtimes');
}

function readHostStatusForSupervisor(statusPath: string): IHostSupervisorHostStatus | null {
  const parsed = readJsonRecordFile(statusPath);
  if (!parsed || readOptionalString(parsed.status) !== 'running') {
    return null;
  }
  const transport = asRecord(parsed.transport);
  const nats = asRecord(transport?.nats);
  const root = readOptionalString(transport?.root);
  const natsUrl = readOptionalString(nats?.url);
  if (!root || !natsUrl) {
    return null;
  }
  const credentialsRef = readOptionalString(nats?.credentialsRef);
  const supervisorMount = readOptionalString(parsed.supervisorMount);
  return {
    ...(supervisorMount ? { supervisorMount } : {}),
    transport: {
      root,
      nats: {
        url: natsUrl,
        ...(credentialsRef ? { credentialsRef } : {}),
      },
    },
  };
}

function hostStatusMatchesExpectation(
  statusPath: string,
  expectation: IHostConfigReloadExpectation,
): boolean {
  const parsed = readJsonRecordFile(statusPath);
  const transport = asRecord(parsed?.transport);
  if (!transport) {
    return false;
  }
  const root = readOptionalString(transport.root);
  const authorityRoot = readOptionalString(transport.authorityRoot) ?? root;
  const routeKey = readOptionalString(transport.routeKey);
  const publicNamespace = readOptionalString(transport.publicNamespace);
  const spaceId = readOptionalString(transport.spaceId);
  if (root !== expectation.root) {
    return false;
  }
  if (expectation.authorityRoot !== undefined && authorityRoot !== expectation.authorityRoot) {
    return false;
  }
  if (expectation.routeKey !== undefined && routeKey !== (expectation.routeKey ?? undefined)) {
    return false;
  }
  if (
    expectation.publicNamespace !== undefined
    && publicNamespace !== (expectation.publicNamespace ?? undefined)
  ) {
    return false;
  }
  if (expectation.spaceId !== undefined && spaceId !== (expectation.spaceId ?? undefined)) {
    return false;
  }
  return true;
}

async function waitForHostStatusExpectation(
  statusPath: string,
  expectation: IHostConfigReloadExpectation,
  timeoutMs = HOST_CONFIG_STATUS_APPLY_TIMEOUT_MS,
): Promise<boolean> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() <= deadline) {
    if (hostStatusMatchesExpectation(statusPath, expectation)) {
      return true;
    }
    await delay(100);
  }
  return false;
}

function readHostRuntimeRecords(matrixHome: string): Record<string, unknown>[] {
  const recordsDir = hostRuntimeRecordsDir(matrixHome);
  if (!fs.existsSync(recordsDir)) {
    return [];
  }
  const records: Record<string, unknown>[] = [];
  for (const entry of fs.readdirSync(recordsDir, { withFileTypes: true })) {
    if (!entry.isDirectory()) continue;
    const record = readJsonRecordFile(path.join(recordsDir, entry.name, 'runtime.json'));
    if (record) {
      records.push(record);
    }
  }
  return records;
}

function isProcessAliveForRuntime(pid: unknown): boolean {
  if (typeof pid !== 'number' || !Number.isInteger(pid) || pid <= 0) {
    return false;
  }
  try {
    process.kill(pid, 0);
    return true;
  } catch (error: unknown) {
    return asRecord(error)?.code === 'EPERM';
  }
}

function positivePort(value: unknown): number | undefined {
  if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0 || value > 65535) {
    return undefined;
  }
  return value;
}

function positiveInteger(value: unknown): number | undefined {
  if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
    return undefined;
  }
  return value;
}

function loopbackUrlPort(raw: string | undefined): number | undefined {
  if (!raw) return undefined;
  try {
    const parsed = new URL(raw);
    if (!['127.0.0.1', 'localhost', '[::1]', '::1'].includes(parsed.hostname)) {
      return undefined;
    }
    const port = Number.parseInt(parsed.port, 10);
    return Number.isFinite(port) && port > 0 && port <= 65535 ? port : undefined;
  } catch {
    return undefined;
  }
}

async function isTcpPortOpen(host: string, port: number): Promise<boolean> {
  return await new Promise<boolean>((resolve) => {
    const socket = createConnection({ host, port });
    const done = (open: boolean): void => {
      socket.removeAllListeners();
      socket.destroy();
      resolve(open);
    };
    socket.setTimeout(500);
    socket.once('connect', () => done(true));
    socket.once('timeout', () => done(false));
    socket.once('error', () => done(false));
  });
}

async function waitForTcpPort(host: string, port: number, timeoutMs: number): Promise<boolean> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() <= deadline) {
    if (await isTcpPortOpen(host, port)) {
      return true;
    }
    await delay(100);
  }
  return false;
}

function resolveHostRelativePath(matrixHome: string, raw: string | undefined, defaultRelative: string): string {
  const value = raw ?? defaultRelative;
  return path.isAbsolute(value) ? value : path.resolve(matrixHome, value);
}

function writeLocalOwnerNatsConfig(matrixHome: string, nats: Record<string, unknown>): {
  readonly binaryPath: string;
  readonly confFile: string;
  readonly pidFile: string;
  readonly dataDir: string;
  readonly port: number;
  readonly wsPort: number;
} {
  const port = positivePort(nats.port) ?? loopbackUrlPort(readOptionalString(nats.url));
  const wsPort = positivePort(nats.wsPort) ?? loopbackUrlPort(readOptionalString(nats.wsUrl));
  const maxPayloadBytes = positiveInteger(nats.maxPayloadBytes) ?? DEFAULT_NATS_MAX_PAYLOAD_BYTES;
  if (!port || !wsPort) {
    throw new Error('Local owner NATS transport requires loopback client and websocket ports');
  }
  const dataDir = resolveHostRelativePath(matrixHome, readOptionalString(nats.dataDir), path.join('nats', 'host-default'));
  const pidFile = resolveHostRelativePath(
    matrixHome,
    readOptionalString(nats.pidFile),
    path.join('nats', 'host-default', 'nats-server.pid'),
  );
  const binaryPath = resolveHostRelativePath(matrixHome, readOptionalString(nats.binaryPath), path.join('bin', 'nats-server'));
  const confFile = path.join(dataDir, 'nats-server.conf');
  fs.mkdirSync(dataDir, { recursive: true });
  const escapedStoreDir = dataDir.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
  fs.writeFileSync(confFile, [
    'host: "127.0.0.1"',
    `port: ${port}`,
    `max_payload: ${maxPayloadBytes}`,
    'jetstream {',
    `  store_dir: "${escapedStoreDir}"`,
    '}',
    'websocket {',
    '  host: "127.0.0.1"',
    `  port: ${wsPort}`,
    '  no_tls: true',
    '}',
    '',
  ].join('\n'), 'utf8');
  return { binaryPath, confFile, pidFile, dataDir, port, wsPort };
}

async function ensureLocalOwnerNatsDependency(matrixHome: string): Promise<void> {
  const config = readJsonRecordFile(path.join(matrixHome, 'host.json'));
  const transport = asRecord(config?.transport);
  const nats = asRecord(transport?.nats);
  if (!transport || !nats) return;
  const root = readOptionalString(transport.root);
  const mode = readOptionalString(nats.mode);
  const credentialsRef = readOptionalString(nats.credentialsRef);
  if (!root || mode !== 'external' || credentialsRef) {
    return;
  }
  const port = positivePort(nats.port) ?? loopbackUrlPort(readOptionalString(nats.url));
  const wsPort = positivePort(nats.wsPort) ?? loopbackUrlPort(readOptionalString(nats.wsUrl));
  if (!port || !wsPort) return;
  if (await isTcpPortOpen('127.0.0.1', port)) {
    return;
  }
  const paths = writeLocalOwnerNatsConfig(matrixHome, nats);
  if (!fs.existsSync(paths.binaryPath)) {
    throw new Error(`Local owner NATS binary is missing: ${paths.binaryPath}`);
  }
  if (fs.existsSync(paths.pidFile)) {
    const pid = Number.parseInt(fs.readFileSync(paths.pidFile, 'utf8').trim(), 10);
    if (Number.isInteger(pid) && pid > 0 && isProcessAliveForRuntime(pid)) {
      throw new Error(`Local owner NATS pid ${pid} is alive but port ${port} is not reachable`);
    }
    fs.rmSync(paths.pidFile, { force: true });
  }
  fs.mkdirSync(path.dirname(paths.pidFile), { recursive: true });
  fs.mkdirSync(path.join(matrixHome, 'logs'), { recursive: true });
  const stdout = fs.openSync(path.join(matrixHome, 'logs', 'nats.stdout.log'), 'a');
  const stderr = fs.openSync(path.join(matrixHome, 'logs', 'nats.stderr.log'), 'a');
  const child = spawn(paths.binaryPath, ['-c', paths.confFile], {
    detached: true,
    stdio: ['ignore', stdout, stderr],
    windowsHide: true,
  });
  if (!child.pid) {
    throw new Error('Local owner NATS did not provide a process id');
  }
  fs.writeFileSync(paths.pidFile, `${child.pid}\n`, 'utf8');
  child.unref();
  const started = await waitForTcpPort('127.0.0.1', port, LOCAL_OWNER_NATS_START_TIMEOUT_MS);
  if (!started) {
    throw new Error(`Local owner NATS pid ${child.pid} did not open port ${port} within ${LOCAL_OWNER_NATS_START_TIMEOUT_MS}ms`);
  }
}

function runtimeRecordMatchesExpectation(
  record: Record<string, unknown>,
  expectation: IHostConfigReloadExpectation,
): boolean {
  const metadata = asRecord(record.metadata);
  const transport = asRecord(metadata?.transport);
  const root = readOptionalString(transport?.root) ?? readOptionalString(record.runtimeWireRoot);
  const authorityRoot = readOptionalString(transport?.authorityRoot) ?? root;
  const routeKey = readOptionalString(transport?.routeKey);
  const publicNamespace = readOptionalString(transport?.publicNamespace);
  const spaceId = readOptionalString(transport?.spaceId);
  if (root !== expectation.root) {
    return false;
  }
  if (expectation.authorityRoot !== undefined && authorityRoot !== expectation.authorityRoot) {
    return false;
  }
  if (expectation.routeKey !== undefined && routeKey !== (expectation.routeKey ?? undefined)) {
    return false;
  }
  if (
    expectation.publicNamespace !== undefined
    && publicNamespace !== (expectation.publicNamespace ?? undefined)
  ) {
    return false;
  }
  if (expectation.spaceId !== undefined && spaceId !== (expectation.spaceId ?? undefined)) {
    return false;
  }
  return true;
}

function activeDefaultRuntimeRecordsReady(
  matrixHome: string,
  expectation: IHostConfigReloadExpectation,
): boolean {
  const activeDefaultRecords = readActiveDefaultRuntimeRecords(matrixHome);
  if (activeDefaultRecords.length === 0) {
    return true;
  }
  return activeDefaultRecords.every((record) => (
    record.status === 'running'
    && isProcessAliveForRuntime(record.pid)
    && runtimeRecordMatchesExpectation(record, expectation)
  ));
}

function readActiveDefaultRuntimeRecords(matrixHome: string): Record<string, unknown>[] {
  return readHostRuntimeRecords(matrixHome).filter((record) => {
    const metadata = asRecord(record.metadata);
    return metadata?.hivecastDefaultRuntimePolicy === 'active' && record.startup === 'auto';
  });
}

function hostConfigRuntimeApplyTimeoutMs(matrixHome: string): number {
  const activeRuntimeCount = readActiveDefaultRuntimeRecords(matrixHome).length;
  if (activeRuntimeCount === 0) {
    return HOST_CONFIG_RUNTIME_APPLY_MIN_TIMEOUT_MS;
  }
  // Host Service starts persisted runtimes in dependency order and waits for
  // each control actor. The CLI reload waiter must scale with that contract.
  return Math.max(
    HOST_CONFIG_RUNTIME_APPLY_MIN_TIMEOUT_MS,
    activeRuntimeCount * HOST_CONFIG_RUNTIME_APPLY_PER_RUNTIME_MS,
  );
}

async function waitForActiveDefaultRuntimeRecords(
  matrixHome: string,
  expectation: IHostConfigReloadExpectation,
  timeoutMs = hostConfigRuntimeApplyTimeoutMs(matrixHome),
): Promise<boolean> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() <= deadline) {
    if (activeDefaultRuntimeRecordsReady(matrixHome, expectation)) {
      return true;
    }
    await delay(100);
  }
  return false;
}

async function activeDefaultWebappOriginsReady(matrixHome: string): Promise<boolean> {
  const webappOrigins = readActiveDefaultRuntimeRecords(matrixHome)
    .map((record) => readOptionalString(asRecord(asRecord(record.metadata)?.webapp)?.origin))
    .filter((origin): origin is string => origin !== undefined);
  if (webappOrigins.length === 0) {
    return true;
  }
  const probes = await Promise.all(webappOrigins.map((origin) => webappOriginReady(origin)));
  return probes.every(Boolean);
}

async function activeDefaultHostWebappRoutesReady(matrixHome: string, statusPath: string): Promise<boolean> {
  const hostOrigin = readHostHttpOrigin(statusPath);
  if (!hostOrigin) {
    return false;
  }
  const routePrefixes = readActiveDefaultRuntimeRecords(matrixHome)
    .map((record) => readOptionalString(asRecord(asRecord(record.metadata)?.webapp)?.routePrefix))
    .filter((routePrefix): routePrefix is string => routePrefix !== undefined);
  if (routePrefixes.length === 0) {
    return true;
  }
  const probes = await Promise.all(routePrefixes.map((routePrefix) => {
    const routeUrl = new URL(routePrefix, hostOrigin);
    return webappOriginReady(routeUrl.href);
  }));
  return probes.every(Boolean);
}

async function waitForActiveDefaultWebappOrigins(
  matrixHome: string,
  statusPath: string,
  timeoutMs = hostConfigRuntimeApplyTimeoutMs(matrixHome),
): Promise<boolean> {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() <= deadline) {
    if (
      await activeDefaultWebappOriginsReady(matrixHome)
      && await activeDefaultHostWebappRoutesReady(matrixHome, statusPath)
    ) {
      return true;
    }
    await delay(100);
  }
  return false;
}

async function describeActiveDefaultWebappReadiness(matrixHome: string, statusPath: string): Promise<string> {
  const hostOrigin = readHostHttpOrigin(statusPath);
  const records = readActiveDefaultRuntimeRecords(matrixHome)
    .map((record) => {
      const webapp = asRecord(asRecord(record.metadata)?.webapp);
      return {
        runtimeId: readOptionalString(record.runtimeId) ?? 'unknown-runtime',
        routePrefix: readOptionalString(webapp?.routePrefix),
        origin: readOptionalString(webapp?.origin),
      };
    })
    .filter((record) => record.routePrefix || record.origin);
  const lines: string[] = [];
  for (const record of records) {
    const direct = record.origin
      ? await describeWebappUrl(record.origin)
      : 'direct=missing';
    const routed = hostOrigin && record.routePrefix
      ? await describeWebappUrl(new URL(record.routePrefix, hostOrigin).href)
      : 'routed=missing';
    lines.push(`${record.runtimeId} route=${record.routePrefix ?? 'none'} ${direct} ${routed}`);
  }
  return lines.slice(0, 12).join('; ');
}

async function describeWebappUrl(url: string): Promise<string> {
  try {
    const response = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(1_500) });
    const body = await response.text();
    const assetUrls = webappAssetUrls(new URL(url), body);
    const firstAsset = assetUrls[0];
    if (!firstAsset) {
      return `${url} html=${response.status} asset=none`;
    }
    try {
      const assetResponse = await fetch(firstAsset, { method: 'GET', signal: AbortSignal.timeout(1_500) });
      await assetResponse.arrayBuffer();
      return `${url} html=${response.status} asset=${assetResponse.status} assetUrl=${firstAsset.href}`;
    } catch (error) {
      return `${url} html=${response.status} asset=fetch-failed:${error instanceof Error ? error.message : String(error)}`;
    }
  } catch (error) {
    return `${url} html=fetch-failed:${error instanceof Error ? error.message : String(error)}`;
  }
}

function readHostHttpOrigin(statusPath: string): string | undefined {
  const parsed = readJsonRecordFile(statusPath);
  const http = asRecord(parsed?.http);
  return readOptionalString(http?.origin);
}

async function webappOriginReady(origin: string): Promise<boolean> {
  let url: URL;
  try {
    url = new URL(origin);
  } catch {
    return false;
  }
  if (!['http:', 'https:'].includes(url.protocol)) {
    return false;
  }
  try {
    const response = await fetch(url, { method: 'GET', signal: AbortSignal.timeout(1_500) });
    const body = await response.text();
    if (response.status < 200 || response.status >= 500) {
      return false;
    }
    const assetUrls = webappAssetUrls(url, body);
    if (assetUrls.length === 0) {
      return true;
    }
    const assetResponses = await Promise.all(assetUrls.map(async (assetUrl) => {
      try {
        const assetResponse = await fetch(assetUrl, { method: 'GET', signal: AbortSignal.timeout(1_500) });
        await assetResponse.arrayBuffer();
        return assetResponse.status >= 200 && assetResponse.status < 500;
      } catch {
        return false;
      }
    }));
    return assetResponses.every(Boolean);
  } catch {
    return false;
  }
}

function webappAssetUrls(origin: URL, body: string): URL[] {
  const urls: URL[] = [];
  const seen = new Set<string>();
  const assetPattern = /\b(?:src|href)=["']([^"']*assets\/[^"']+)["']/g;
  let match = assetPattern.exec(body);
  while (match) {
    const raw = match[1];
    if (raw) {
      try {
        const url = new URL(raw, origin);
        if (url.origin === origin.origin && !seen.has(url.href)) {
          seen.add(url.href);
          urls.push(url);
        }
      } catch {
        // Ignore malformed asset references in readiness probing.
      }
    }
    match = assetPattern.exec(body);
  }
  return urls.slice(0, 8);
}

async function reloadRunningHostConfig(
  matrixHome: string,
  expectation: IHostConfigReloadExpectation,
): Promise<IHiveCastHostConfigReloadResult> {
  const statusPath = hostStatusPath(matrixHome);
  if (
    !expectation.forceReload
    && hostStatusMatchesExpectation(statusPath, expectation)
    && activeDefaultRuntimeRecordsReady(matrixHome, expectation)
  ) {
    return { attempted: false, applied: true, statusPath };
  }
  const status = readHostStatusForSupervisor(statusPath);
  if (!status) {
    return { attempted: false, applied: false, statusPath };
  }
  try {
    await invokeHostControlFromStatus(status, 'config.reload', {}, 5_000);
    const statusApplied = await waitForHostStatusExpectation(statusPath, expectation);
    const applied = statusApplied;
    const expectationLabel = expectation.label ?? 'linked';
    return {
      attempted: true,
      applied,
      statusPath,
      ...(applied ? {} : { error: statusApplied
        ? `Running Host applied ${expectationLabel} config but did not report success`
        : `Timed out waiting for running Host to apply ${expectationLabel} config` }),
    };
  } catch (error) {
    return {
      attempted: true,
      applied: false,
      statusPath,
      error: error instanceof Error ? error.message : String(error),
    };
  }
}

async function waitForLoopbackSetupCode(
  cloudUrl: string,
  options: IHiveCastLoginCommandOptions,
): Promise<INormalizedSetupExchange> {
  const timeoutMs = options.callbackTimeoutMs ?? 10 * 60 * 1000;
  const pairNonce = randomBytes(18).toString('base64url');
  return await new Promise<INormalizedSetupExchange>((resolve, reject) => {
    let settled = false;
    const server = createServer((req, res) => {
      void (async () => {
        const requestUrl = new URL(req.url ?? '/', 'http://127.0.0.1');
        if (requestUrl.pathname !== '/auth/callback') {
          res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
          res.end('Not found');
          return;
        }
        const pairRequestId = readOptionalString(requestUrl.searchParams.get('pairRequestId'));
        const approvalCode = readOptionalString(requestUrl.searchParams.get('approvalCode'));
        const nonce = readOptionalString(requestUrl.searchParams.get('nonce'));
        if (!pairRequestId || !approvalCode) {
          res.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
          res.end('<h1>HiveCast login failed</h1><p>Missing approval code.</p>');
          return;
        }
        if (nonce !== pairNonce) {
          res.writeHead(400, { 'content-type': 'text/html; charset=utf-8' });
          res.end('<h1>HiveCast login failed</h1><p>Invalid approval nonce.</p>');
          return;
        }
        const exchange = await exchangePairApprovalWithRequiredCodes(cloudUrl, pairRequestId, approvalCode);
        res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
        res.end('<h1>HiveCast connected</h1><p>You can return to the terminal.</p>');
        settled = true;
        clearTimeout(timer);
        resolve(exchange);
        server.close();
      })().catch((error: unknown) => {
        if (!settled) {
          settled = true;
          clearTimeout(timer);
          if (!res.headersSent) {
            res.writeHead(500, { 'content-type': 'text/html; charset=utf-8' });
            res.end('<h1>HiveCast login failed</h1><p>Pairing exchange failed.</p>');
          }
          server.close();
          reject(error instanceof Error ? error : new Error(String(error)));
        }
      });
    });
    const timer = setTimeout(() => {
      if (settled) return;
      settled = true;
      server.close();
      reject(new Error('HIVECAST_LOGIN_TIMEOUT: timed out waiting for browser approval'));
    }, timeoutMs);
    server.once('error', (err) => {
      if (settled) return;
      settled = true;
      clearTimeout(timer);
      reject(err);
    });
    server.listen(options.localPort ?? 0, '127.0.0.1', () => {
      const address = server.address();
      if (!address || typeof address !== 'object') {
        clearTimeout(timer);
        settled = true;
        server.close();
        reject(new Error('HIVECAST_LOGIN_CALLBACK_FAILED: failed to bind loopback callback'));
        return;
      }
      void startPairFlow(cloudUrl, {
        ...options,
        callbackUrl: `http://127.0.0.1:${address.port}/auth/callback`,
        pairNonce,
      }).then((pair) => {
        options.onSetupUrl?.(pair.approvalUrl);
        if (!options.noOpen) {
          (options.openApprovalUrl ?? openBrowser)(pair.approvalUrl);
        }
      }).catch((error: unknown) => {
        if (settled) return;
        settled = true;
        clearTimeout(timer);
        server.close();
        reject(error instanceof Error ? error : new Error(String(error)));
      });
    });
  });
}

function normalizeSetupExchange(value: ITokenExchangeResponse): INormalizedSetupExchange {
  const authorityRoot = readOptionalString(value.authorityRoot) ?? readOptionalString(value.root);
  if (!authorityRoot) {
    throw new Error('HIVECAST_LOGIN_INVALID_EXCHANGE: response is missing authority root');
  }
  const nats = asRecord(value.nats);
  const natsUrl = readOptionalString(nats?.url);
  if (!natsUrl) {
    throw new Error('HIVECAST_LOGIN_INVALID_EXCHANGE: response is missing nats.url');
  }
  const credentials = asRecord(value.credentials);
  const jwt = readOptionalString(credentials?.jwt);
  const seed = readOptionalString(credentials?.seed);
  if (!jwt || !seed) {
    throw new Error('HIVECAST_LOGIN_INVALID_EXCHANGE: credentials must contain device-scoped jwt+seed');
  }
  const wsUrl = readOptionalString(nats?.wsUrl);
  const leafnodeUrl = readOptionalString(nats?.leafnodeUrl);
  const routeKey = readOptionalString(value.routeKey);
  const deviceRegistryRoot = readOptionalString(value.deviceRegistryRoot);
  const publicNamespace = readOptionalString(value.publicNamespace);
  const spaceId = readOptionalString(value.spaceId);
  const principalId = readOptionalString(value.principalId);
  const hostLink = asRecord(value.hostLink);
  const device = asRecord(value.device);
  const hostLinkId = readOptionalString(value.hostLinkId)
    ?? readOptionalString(hostLink?.id);
  const hostId = readOptionalString(value.hostId)
    ?? readOptionalString(hostLink?.hostId)
    ?? readOptionalString(device?.deviceCode);
  const hostName = readOptionalString(value.hostName)
    ?? readOptionalString(hostLink?.hostName);
  const deviceSlug = readOptionalString(value.deviceSlug)
    ?? readOptionalString(hostLink?.deviceSlug);
  const authorityCoordinator = typeof value.authorityCoordinator === 'boolean'
    ? value.authorityCoordinator
    : typeof hostLink?.authorityCoordinator === 'boolean'
      ? hostLink.authorityCoordinator
      : undefined;
  const hostLinkToken = readOptionalString(value.hostLinkToken)
    ?? readOptionalString(hostLink?.heartbeatToken)
    ?? readOptionalString(hostLink?.hostLinkToken);
  return {
    authorityRoot,
    ...(deviceRegistryRoot ? { deviceRegistryRoot } : {}),
    ...(hostLinkId ? { hostLinkId } : {}),
    ...(hostId ? { hostId } : {}),
    ...(routeKey ? { routeKey } : {}),
    ...(publicNamespace ? { publicNamespace } : {}),
    ...(spaceId ? { spaceId } : {}),
    ...(principalId ? { principalId } : {}),
    ...(hostName ? { hostName } : {}),
    ...(deviceSlug ? { deviceSlug } : {}),
    ...(typeof authorityCoordinator === 'boolean' ? { authorityCoordinator } : {}),
    ...(hostLinkToken ? { hostLinkToken } : {}),
    nats: {
      url: natsUrl,
      ...(wsUrl ? { wsUrl } : {}),
      ...(leafnodeUrl ? { leafnodeUrl } : {}),
    },
    credentials: { jwt, seed },
  };
}

async function exchangeSetupCode(cloudUrl: string, code: string): Promise<INormalizedSetupExchange> {
  const response = await fetch(`${cloudUrl}/api/token-exchange`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ code }),
  });
  const parsed = await readJsonResponse<ITokenExchangeResponse>(response, 'HiveCast setup exchange');
  if (!response.ok || parsed.ok !== true) {
    const message = readOptionalString(parsed.error) ?? `HiveCast setup exchange failed: HTTP ${response.status}`;
    throw new Error(`HIVECAST_LOGIN_EXCHANGE_FAILED: ${message}`);
  }
  return normalizeSetupExchange(parsed);
}

async function exchangePairApproval(
  cloudUrl: string,
  pairRequestId: string,
  approvalCode: string,
): Promise<INormalizedSetupExchange> {
  const response = await fetch(`${cloudUrl}/_auth/pair/exchange`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ pairRequestId, approvalCode }),
  });
  const parsed = await readJsonResponse<ITokenExchangeResponse>(response, 'HiveCast pair exchange');
  if (!response.ok || parsed.ok !== true) {
    const message = readOptionalString(parsed.error) ?? `HiveCast pair exchange failed: HTTP ${response.status}`;
    throw new Error(`HIVECAST_PAIR_EXCHANGE_FAILED: ${message}`);
  }
  return normalizeSetupExchange(parsed);
}

async function exchangePairApprovalWithRequiredCodes(
  cloudUrl: string,
  pairRequestId: string | undefined,
  approvalCode: string | undefined,
): Promise<INormalizedSetupExchange> {
  if (!pairRequestId || !approvalCode) {
    throw new Error('HIVECAST_PAIR_APPROVAL_REQUIRED: missing pair approval code');
  }
  return await exchangePairApproval(cloudUrl, pairRequestId, approvalCode);
}

async function startPairFlow(
  cloudUrl: string,
  options: IHiveCastLoginCommandOptions,
): Promise<{ readonly pairRequestId: string; readonly approvalUrl: string; readonly expiresIn: number }> {
  const localReturnUrl = readOptionalString(options.callbackUrl);
  const nonce = readOptionalString(options.pairNonce);
  const routeKey = readOptionalString(options.routeKey);
  const requestedHostName = readOptionalString(options.hostName) ?? os.hostname();
  const { identity } = ensureHiveCastInstallIdentity({
    cwd: options.cwd ?? process.cwd(),
    matrixHome: options.home,
    hostName: requestedHostName,
  });
  const response = await fetch(`${cloudUrl}/_auth/pair/start`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({
      hostId: identity.installId,
      hostName: identity.hostName ?? requestedHostName,
      authorityCoordinator: false,
      ...(localReturnUrl ? { localReturnUrl } : {}),
      ...(nonce ? { nonce } : {}),
      ...(routeKey ? { routeKey } : {}),
    }),
  });
  const parsed = await readJsonResponse<IPairStartResponse>(response, 'HiveCast pair start');
  if (!response.ok || parsed.ok !== true) {
    const message = readOptionalString(parsed.error) ?? `HiveCast pairing start failed: HTTP ${response.status}`;
    throw new Error(`HIVECAST_PAIR_START_FAILED: ${message}`);
  }
  const pairRequestId = readOptionalString(parsed.pairRequestId);
  const approvalUrl = readOptionalString(parsed.approvalUrl);
  if (!pairRequestId || !approvalUrl) {
    throw new Error('HIVECAST_PAIR_INVALID_START: response is missing pairing challenge fields');
  }
  const expiresIn = typeof parsed.expiresIn === 'number' && Number.isFinite(parsed.expiresIn)
    ? parsed.expiresIn
    : 180 * 24 * 60 * 60;
  return { pairRequestId, approvalUrl, expiresIn };
}

function normalizeDeviceChallenge(value: IDeviceStartResponse): IHiveCastDeviceChallenge {
  const deviceCode = readOptionalString(value.deviceCode);
  const userCode = readOptionalString(value.userCode);
  const verificationUri = readOptionalString(value.verificationUri);
  const verificationUriComplete = readOptionalString(value.verificationUriComplete);
  const expiresIn = typeof value.expiresIn === 'number' && Number.isFinite(value.expiresIn)
    ? value.expiresIn
    : 180 * 24 * 60 * 60;
  const interval = typeof value.interval === 'number' && Number.isFinite(value.interval) && value.interval > 0
    ? value.interval
    : 2;
  if (!deviceCode || !userCode || !verificationUri) {
    throw new Error('HIVECAST_DEVICE_INVALID_START: response is missing device challenge fields');
  }
  return {
    deviceCode,
    userCode,
    verificationUri,
    ...(verificationUriComplete ? { verificationUriComplete } : {}),
    expiresIn,
    interval,
  };
}

async function startDeviceFlow(
  cloudUrl: string,
  options: IHiveCastLoginCommandOptions,
): Promise<IHiveCastDeviceChallenge> {
  const requestedHostName = readOptionalString(options.hostName) ?? os.hostname();
  const { identity } = ensureHiveCastInstallIdentity({
    cwd: options.cwd ?? process.cwd(),
    matrixHome: options.home,
    hostName: requestedHostName,
  });
  let lastError: unknown;
  for (let attempt = 1; attempt <= 4; attempt += 1) {
    try {
      const response = await fetch(`${cloudUrl}/_auth/device/start`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          hostId: identity.installId,
          hostName: identity.hostName ?? requestedHostName,
          authorityCoordinator: false,
          ...(readOptionalString(options.routeKey) ? { routeKey: readOptionalString(options.routeKey) } : {}),
        }),
      });
      const parsed = await readJsonResponse<IDeviceStartResponse>(response, 'HiveCast device start');
      if (!response.ok || parsed.ok !== true) {
        const message = readOptionalString(parsed.error) ?? `HiveCast device start failed: HTTP ${response.status}`;
        throw new Error(`HIVECAST_DEVICE_START_FAILED: ${message}`);
      }
      return normalizeDeviceChallenge(parsed);
    } catch (error: unknown) {
      lastError = error;
      const message = error instanceof Error ? error.message : String(error);
      if (!isRetryableDeviceStartError(message) || attempt === 4) {
        break;
      }
      await delay(attempt * 500);
    }
  }
  const message = lastError instanceof Error ? lastError.message : String(lastError ?? 'unknown error');
  throw message.startsWith('HIVECAST_DEVICE_START_FAILED:')
    ? new Error(message)
    : new Error(`HIVECAST_DEVICE_START_FAILED: ${message}`);
}

function isRetryableDeviceStartError(message: string): boolean {
  return /\bHTTP (502|503|504)\b/i.test(message)
    || /empty response body/i.test(message)
    || /fetch failed|network|timeout/i.test(message);
}

async function pollDeviceApproval(
  cloudUrl: string,
  challenge: IHiveCastDeviceChallenge,
  timeoutMs: number,
): Promise<void> {
  const deadline = Date.now() + timeoutMs;
  let intervalMs = Math.max(1, challenge.interval) * 1000;
  while (Date.now() < deadline) {
    const response = await fetch(`${cloudUrl}/_auth/device/poll`, {
      method: 'POST',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ deviceCode: challenge.deviceCode }),
    });
    let parsed: IDevicePollResponse;
    try {
      parsed = await readJsonResponse<IDevicePollResponse>(response, 'HiveCast device poll');
    } catch (error: unknown) {
      const remainingMs = deadline - Date.now();
      if (response.ok && remainingMs > 1_000) {
        await delay(Math.min(1_000, remainingMs));
        continue;
      }
      throw error;
    }
    const status = readOptionalString(parsed.status);
    if ((!response.ok || parsed.ok !== true) && status !== 'namespace_required') {
      const message = readOptionalString(parsed.error) ?? `HiveCast device poll failed: HTTP ${response.status}`;
      throw new Error(`HIVECAST_DEVICE_POLL_FAILED: ${message}`);
    }
    if (status === 'approved') {
      return;
    }
    if (status !== 'authorization_pending' && status !== 'namespace_required') {
      throw new Error(`HIVECAST_DEVICE_POLL_FAILED: unexpected status ${status ?? 'unknown'}`);
    }
    if (typeof parsed.interval === 'number' && Number.isFinite(parsed.interval) && parsed.interval > 0) {
      intervalMs = parsed.interval * 1000;
    }
    await delay(Math.min(intervalMs, Math.max(0, deadline - Date.now())));
  }
  throw new Error('HIVECAST_DEVICE_TIMEOUT: timed out waiting for device approval');
}

async function exchangeDeviceCode(
  cloudUrl: string,
  deviceCode: string,
): Promise<INormalizedSetupExchange> {
  const response = await fetch(`${cloudUrl}/_auth/device/exchange`, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ deviceCode }),
  });
  const parsed = await readJsonResponse<ITokenExchangeResponse>(response, 'HiveCast device exchange');
  if (!response.ok || parsed.ok !== true) {
    const message = readOptionalString(parsed.error) ?? `HiveCast device exchange failed: HTTP ${response.status}`;
    throw new Error(`HIVECAST_DEVICE_EXCHANGE_FAILED: ${message}`);
  }
  return normalizeSetupExchange(parsed);
}

function deviceChallengeTimeoutMs(challenge: IHiveCastDeviceChallenge): number {
  if (Number.isFinite(challenge.expiresIn) && challenge.expiresIn > 0) {
    return (challenge.expiresIn * 1000) + 5_000;
  }
  return 180 * 24 * 60 * 60 * 1000;
}

async function delay(ms: number): Promise<void> {
  if (ms <= 0) {
    return;
  }
  await new Promise<void>((resolve) => {
    setTimeout(resolve, ms);
  });
}

export async function loginCommand(options: ILoginCommandOptions): Promise<ILoginCommandResult> {
  if (!options.username.trim()) {
    throw new Error('MX_AUTH_REQUIRED: username is required for login');
  }
  if (!options.token.trim()) {
    throw new Error('MX_AUTH_REQUIRED: token is required for login');
  }

  const cwd = options.cwd ?? process.cwd();
  const credential: IRegistryCredential = {
    username: options.username.trim(),
    token: options.token.trim(),
    email: options.email?.trim() || undefined,
    expiresAt: options.expiresAt?.trim() || undefined,
  };

  const { registry, credentialsPath } = saveRegistryCredential(cwd, credential, {
    registry: options.registry,
    credentialsPath: options.credentialsPath,
  });

  return {
    registry,
    username: credential.username,
    credentialsPath,
  };
}

export async function logoutCommand(options: IAuthContextOptions = {}): Promise<ILogoutCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  return removeRegistryCredential(cwd, {
    registry: options.registry,
    credentialsPath: options.credentialsPath,
  });
}

export async function whoamiCommand(options: IAuthContextOptions = {}): Promise<IWhoamiCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const { registry, credential } = readRegistryCredential(cwd, {
    registry: options.registry,
    credentialsPath: options.credentialsPath,
  });

  if (!credential) {
    return { registry, authenticated: false };
  }

  return {
    registry,
    authenticated: true,
    username: credential.username,
    email: credential.email,
    expiresAt: credential.expiresAt,
  };
}

export async function hiveCastLoginCommand(
  options: IHiveCastLoginCommandOptions = {},
): Promise<IHiveCastLoginCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const cloudUrl = normalizeCloudUrl(options.cloudUrl);
  const existing = readHiveCastHostLink(cwd, options.home);
  if (existing.record && options.forceRelink !== true) {
    let activeRecord = existing.record;
    let activePaths = existing.paths;
    reportLoginProgress(options, 'link', `existing Device link found for ${activeRecord.authorityRoot}`);
    const requestedRouteKey = readOptionalString(options.routeKey);
    if (normalizeCloudUrl(activeRecord.cloudUrl) !== cloudUrl) {
      throw new Error(
        `HIVECAST_LINK_CLOUD_MISMATCH: Host is already linked to ${activeRecord.cloudUrl}; use --relink to replace it`,
      );
    }
    if (requestedRouteKey && activeRecord.routeKey !== requestedRouteKey) {
      throw new Error(
        `HIVECAST_LINK_ROUTE_MISMATCH: Host is already linked to route key ${activeRecord.routeKey ?? '(none)'}; use --relink to replace it`,
      );
    }
    if (options.device === true) {
      reportLoginProgress(options, 'link', 'refreshing linked Device credentials');
      await hiveCastRefreshCredentialsCommand({
        cwd,
        home: options.home,
        authorityCoordinator: false,
      });
      const refreshed = readHiveCastHostLink(cwd, options.home);
      if (refreshed.record) {
        activeRecord = refreshed.record;
        activePaths = refreshed.paths;
      }
    }
    const expectation = hostConfigExpectationForLink(activePaths.matrixHome, activeRecord, false);
    reportLoginProgress(options, 'namespace', `applying authority root ${expectation.root}`);
    const hostConfigReload = await reloadRunningHostConfig(activePaths.matrixHome, expectation);
    assertRunningHostConfigReloadApplied(hostConfigReload, expectation);
    reportLoginProgress(
      options,
      'namespace',
      hostConfigReload.applied ? `Host config converged at ${expectation.root}` : 'no running Host config to reload',
    );
    return {
      mode: 'linked',
      linked: true,
      alreadyLinked: true,
      cloudUrl: activeRecord.cloudUrl,
      matrixHome: activePaths.matrixHome,
      linkPath: activePaths.linkPath,
      hostConfigPath: activePaths.hostConfigPath,
      natsCredentialsPath: activePaths.natsCredentialsPath,
      ...(activeRecord.hostLinkId ? { hostLinkId: activeRecord.hostLinkId } : {}),
      ...(activeRecord.hostId ? { hostId: activeRecord.hostId } : {}),
      authorityRoot: activeRecord.authorityRoot,
      ...(activeRecord.routeKey ? { routeKey: activeRecord.routeKey } : {}),
      ...(activeRecord.publicNamespace ? { publicNamespace: activeRecord.publicNamespace } : {}),
      ...(activeRecord.spaceId ? { spaceId: activeRecord.spaceId } : {}),
      ...(typeof activeRecord.authorityCoordinator === 'boolean'
        ? { authorityCoordinator: activeRecord.authorityCoordinator }
        : {}),
      linkedAt: activeRecord.linkedAt,
      hostConfigReload,
    };
  }
  let exchange: INormalizedSetupExchange | null = null;
  if (options.device) {
    reportLoginProgress(options, 'link', `starting device approval with ${cloudUrl}`);
    const challenge = await startDeviceFlow(cloudUrl, options);
    options.onDeviceStart?.(challenge);
    if (options.waitForCallback === false) {
      return {
        mode: 'approval-required',
        linked: false,
        cloudUrl,
        setupUrl: challenge.verificationUriComplete ?? challenge.verificationUri,
        deviceCode: challenge.deviceCode,
        userCode: challenge.userCode,
        verificationUri: challenge.verificationUri,
        ...(challenge.verificationUriComplete ? { verificationUriComplete: challenge.verificationUriComplete } : {}),
        expiresIn: challenge.expiresIn,
      };
    }
    reportLoginProgress(options, 'link', `waiting for approval code ${challenge.userCode}`);
    await pollDeviceApproval(
      cloudUrl,
      challenge,
      options.pollTimeoutMs ?? options.callbackTimeoutMs ?? deviceChallengeTimeoutMs(challenge),
    );
    reportLoginProgress(options, 'link', 'approved');
    reportLoginProgress(options, 'link', 'exchanging device credentials');
    exchange = await exchangeDeviceCode(cloudUrl, challenge.deviceCode);
  }
  const setupCode = readOptionalString(options.setupCode);
  if (!exchange && setupCode) {
    exchange = await exchangeSetupCode(cloudUrl, setupCode);
  }
  if (!exchange && options.waitForCallback) {
    exchange = await waitForLoopbackSetupCode(cloudUrl, options);
  }
  if (!exchange) {
    const pair = await startPairFlow(cloudUrl, options);
    return {
      mode: 'approval-required',
      linked: false,
      cloudUrl,
      setupUrl: pair.approvalUrl,
    };
  }
  const routeKey = readOptionalString(options.routeKey) ?? exchange.routeKey;
  const publicNamespace = exchange.publicNamespace ?? (routeKey ? `space.${routeKey}` : undefined);
  const requestedHostName = readOptionalString(options.hostName);
  const { identity: installIdentity } = ensureHiveCastInstallIdentity({
    cwd,
    matrixHome: options.home,
    ...(requestedHostName ? { hostName: requestedHostName } : {}),
  });
  const hostName = installIdentity.hostName ?? exchange.hostName ?? requestedHostName;
  const { record, paths } = saveHiveCastHostLink({
    cwd,
    matrixHome: options.home,
    cloudUrl,
    ...(exchange.deviceRegistryRoot ? { deviceRegistryRoot: exchange.deviceRegistryRoot } : {}),
    authorityRoot: exchange.authorityRoot,
    nats: exchange.nats,
    natsCredentials: exchange.credentials,
    ...(exchange.hostLinkToken ? { hostLinkToken: exchange.hostLinkToken } : {}),
    ...(exchange.hostLinkId ? { hostLinkId: exchange.hostLinkId } : {}),
    ...(exchange.hostId ? { hostId: exchange.hostId } : {}),
    ...(exchange.principalId ? { principalId: exchange.principalId } : {}),
    ...(hostName ? { hostName } : {}),
    ...(exchange.deviceSlug ? { deviceSlug: exchange.deviceSlug } : {}),
    ...(typeof exchange.authorityCoordinator === 'boolean' ? { authorityCoordinator: exchange.authorityCoordinator } : {}),
    ...(exchange.spaceId ? { spaceId: exchange.spaceId } : {}),
    ...(routeKey ? { routeKey } : {}),
    ...(publicNamespace ? { publicNamespace } : {}),
  });
  reportLoginProgress(options, 'link', `writing credentials: ${paths.linkPath}`);
  const expectation = hostConfigExpectationForLink(paths.matrixHome, record, true);
  reportLoginProgress(options, 'namespace', `applying authority root ${expectation.root}`);
  const hostConfigReload = await reloadRunningHostConfig(paths.matrixHome, expectation);
  assertRunningHostConfigReloadApplied(hostConfigReload, expectation);
  reportLoginProgress(
    options,
    'namespace',
    hostConfigReload.applied ? `Host config converged at ${expectation.root}` : 'no running Host config to reload',
  );
  reportLoginProgress(options, 'ready', `Device link persisted for ${record.authorityRoot}`);

  return {
    mode: 'linked',
    linked: true,
    cloudUrl,
    matrixHome: paths.matrixHome,
    linkPath: paths.linkPath,
    hostConfigPath: paths.hostConfigPath,
    natsCredentialsPath: paths.natsCredentialsPath,
    ...(record.hostLinkId ? { hostLinkId: record.hostLinkId } : {}),
    ...(record.hostId ? { hostId: record.hostId } : {}),
    authorityRoot: record.authorityRoot,
    ...(record.routeKey ? { routeKey: record.routeKey } : {}),
    ...(record.publicNamespace ? { publicNamespace: record.publicNamespace } : {}),
    ...(record.spaceId ? { spaceId: record.spaceId } : {}),
    ...(typeof record.authorityCoordinator === 'boolean' ? { authorityCoordinator: record.authorityCoordinator } : {}),
    ...(record.hostName ? { hostName: record.hostName } : {}),
    ...(record.deviceSlug ? { deviceSlug: record.deviceSlug } : {}),
    linkedAt: record.linkedAt,
    hostConfigReload,
  };
}

async function revokeHiveCastCloudHostLink(
  existing: ReturnType<typeof readHiveCastHostLink>,
): Promise<IHiveCastCloudRevokeResult> {
  const { record, paths } = existing;
  if (!record) {
    return { attempted: false, revoked: false, skippedReason: 'not-linked' };
  }
  if (!record.hostLinkId || !record.hostId) {
    return { attempted: false, revoked: false, skippedReason: 'missing-host-link-id-or-host-id' };
  }
  const hostLinkToken = fs.existsSync(paths.hostLinkTokenPath)
    ? fs.readFileSync(paths.hostLinkTokenPath, 'utf8').trim()
    : '';
  if (!hostLinkToken) {
    return { attempted: false, revoked: false, skippedReason: 'missing-host-link-token' };
  }
  try {
    const response = await fetch(`${record.cloudUrl}/_auth/host-link/revoke`, {
      method: 'POST',
      headers: {
        'content-type': 'application/json',
        authorization: `Bearer ${hostLinkToken}`,
      },
      body: JSON.stringify({
        hostLinkId: record.hostLinkId,
        hostId: record.hostId,
      }),
    });
    const parsed = await readJsonResponse<{ readonly ok?: unknown; readonly error?: unknown }>(
      response,
      'HiveCast Host-link revoke',
    );
    if (!response.ok || parsed.ok !== true) {
      return {
        attempted: true,
        revoked: false,
        error: readOptionalString(parsed.error) ?? `HiveCast Host-link revoke failed: HTTP ${response.status}`,
      };
    }
    return { attempted: true, revoked: true };
  } catch (error) {
    return {
      attempted: true,
      revoked: false,
      error: error instanceof Error ? error.message : String(error),
    };
  }
}

export async function hiveCastRefreshCredentialsCommand(
  options: IHiveCastRefreshCredentialsCommandOptions = {},
): Promise<IHiveCastRefreshCredentialsCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const { record, paths } = readHiveCastHostLink(cwd, options.home);
  if (!record) {
    throw new Error('HIVECAST_REFRESH_NOT_LINKED: no local HiveCast Host link found');
  }
  if (!record.hostLinkId || !record.hostId) {
    throw new Error('HIVECAST_REFRESH_MISSING_LINK_ID: local HiveCast Host link is missing hostLinkId or hostId');
  }
  const refreshThresholdSeconds = options.ifExpiringWithinSeconds;
  const currentExpirationSeconds = readCredentialsExpirationSeconds(paths.natsCredentialsPath)
    ?? readCredentialsExpirationSeconds(paths.hostCredentialsPath);
  if (refreshThresholdSeconds !== undefined && currentExpirationSeconds !== null) {
    const currentCredentialsSecondsRemaining = currentExpirationSeconds - Math.floor(Date.now() / 1000);
    if (currentCredentialsSecondsRemaining > refreshThresholdSeconds) {
      return {
        refreshed: false,
        matrixHome: paths.matrixHome,
        linkPath: paths.linkPath,
        natsCredentialsPath: paths.natsCredentialsPath,
        currentCredentialsExpiresAt: new Date(currentExpirationSeconds * 1000).toISOString(),
        currentCredentialsSecondsRemaining,
        refreshThresholdSeconds,
        ...(record.hostLinkId ? { hostLinkId: record.hostLinkId } : {}),
        ...(record.hostId ? { hostId: record.hostId } : {}),
        authorityRoot: record.authorityRoot,
        ...(record.routeKey ? { routeKey: record.routeKey } : {}),
        ...(record.publicNamespace ? { publicNamespace: record.publicNamespace } : {}),
        ...(record.spaceId ? { spaceId: record.spaceId } : {}),
        ...(record.hostName ? { hostName: record.hostName } : {}),
        ...(record.deviceSlug ? { deviceSlug: record.deviceSlug } : {}),
        ...(typeof record.authorityCoordinator === 'boolean' ? { authorityCoordinator: record.authorityCoordinator } : {}),
      };
    }
  }
  const hostLinkToken = fs.existsSync(paths.hostLinkTokenPath)
    ? fs.readFileSync(paths.hostLinkTokenPath, 'utf8').trim()
    : '';
  if (!hostLinkToken) {
    throw new Error('HIVECAST_REFRESH_MISSING_TOKEN: local HiveCast Host link is missing its refresh token');
  }
  const response = await fetch(`${record.cloudUrl}/_auth/host-link/credentials/refresh`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      authorization: `Bearer ${hostLinkToken}`,
    },
    body: JSON.stringify({
      hostLinkId: record.hostLinkId,
      hostId: record.hostId,
      ...(options.authorityCoordinator === false ? { authorityCoordinator: false } : {}),
    }),
  });
  const parsed = await readJsonResponse<ITokenExchangeResponse>(response, 'HiveCast credential refresh');
  if (!response.ok || parsed.ok !== true) {
    const message = readOptionalString(parsed.error) ?? `HiveCast credential refresh failed: HTTP ${response.status}`;
    throw new Error(`HIVECAST_REFRESH_FAILED: ${message}`);
  }
  const exchange = normalizeSetupExchange(parsed);
  const routeKey = exchange.routeKey ?? record.routeKey;
  const publicNamespace = exchange.publicNamespace ?? record.publicNamespace;
  const hostName = exchange.hostName ?? record.hostName;
  const { record: saved, paths: savedPaths } = saveHiveCastHostLink({
    cwd,
    matrixHome: options.home,
    cloudUrl: record.cloudUrl,
    ...(exchange.deviceRegistryRoot ?? record.deviceRegistryRoot ? { deviceRegistryRoot: exchange.deviceRegistryRoot ?? record.deviceRegistryRoot } : {}),
    authorityRoot: exchange.authorityRoot,
    nats: exchange.nats,
    natsCredentials: exchange.credentials,
    hostLinkToken: exchange.hostLinkToken ?? hostLinkToken,
    ...(exchange.hostLinkId ?? record.hostLinkId ? { hostLinkId: exchange.hostLinkId ?? record.hostLinkId } : {}),
    ...(exchange.hostId ?? record.hostId ? { hostId: exchange.hostId ?? record.hostId } : {}),
    ...(exchange.principalId ?? record.principalId ? { principalId: exchange.principalId ?? record.principalId } : {}),
    ...(hostName ? { hostName } : {}),
    ...(exchange.deviceSlug ?? record.deviceSlug ? { deviceSlug: exchange.deviceSlug ?? record.deviceSlug } : {}),
    ...(typeof (exchange.authorityCoordinator ?? record.authorityCoordinator) === 'boolean'
      ? { authorityCoordinator: exchange.authorityCoordinator ?? record.authorityCoordinator }
      : {}),
    ...(exchange.spaceId ?? record.spaceId ? { spaceId: exchange.spaceId ?? record.spaceId } : {}),
    ...(routeKey ? { routeKey } : {}),
    ...(publicNamespace ? { publicNamespace } : {}),
    linkedAt: record.linkedAt,
  });
  const hostConfigReload = await reloadRunningHostConfig(
    savedPaths.matrixHome,
    hostConfigExpectationForLink(savedPaths.matrixHome, saved, true),
  );
  assertRunningHostConfigReloadApplied(
    hostConfigReload,
    hostConfigExpectationForLink(savedPaths.matrixHome, saved, true),
  );
  const refreshedExpirationSeconds = readCredentialsExpirationSeconds(savedPaths.natsCredentialsPath);
  return {
    refreshed: true,
    matrixHome: savedPaths.matrixHome,
    linkPath: savedPaths.linkPath,
    natsCredentialsPath: savedPaths.natsCredentialsPath,
    ...(refreshedExpirationSeconds !== null
      ? {
        currentCredentialsExpiresAt: new Date(refreshedExpirationSeconds * 1000).toISOString(),
        currentCredentialsSecondsRemaining: refreshedExpirationSeconds - Math.floor(Date.now() / 1000),
      }
      : {}),
    ...(refreshThresholdSeconds !== undefined ? { refreshThresholdSeconds } : {}),
    ...(saved.hostLinkId ? { hostLinkId: saved.hostLinkId } : {}),
    ...(saved.hostId ? { hostId: saved.hostId } : {}),
    authorityRoot: saved.authorityRoot,
    ...(saved.routeKey ? { routeKey: saved.routeKey } : {}),
    ...(saved.publicNamespace ? { publicNamespace: saved.publicNamespace } : {}),
    ...(saved.spaceId ? { spaceId: saved.spaceId } : {}),
    ...(saved.hostName ? { hostName: saved.hostName } : {}),
    ...(saved.deviceSlug ? { deviceSlug: saved.deviceSlug } : {}),
    ...(typeof saved.authorityCoordinator === 'boolean' ? { authorityCoordinator: saved.authorityCoordinator } : {}),
    hostConfigReload,
  };
}

export async function hiveCastLogoutCommand(
  options: IHiveCastLogoutCommandOptions = {},
): Promise<IHiveCastLogoutCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const existing = readHiveCastHostLink(cwd, options.home);
  const shouldRevokeCloudLink = options.all === true || options.revokeCloudLink === true;
  const cloudRevoke = shouldRevokeCloudLink
    ? await revokeHiveCastCloudHostLink(existing)
    : undefined;
  const result = removeHiveCastHostLink(cwd, options.home, { preserveCredentials: true });
  await ensureLocalOwnerNatsDependency(result.paths.matrixHome);
  const localOwnerTransport = localOwnerTransportForExpectation(result.paths.matrixHome);
  if (!localOwnerTransport) {
    throw new Error(`HIVECAST_LINK_LOCAL_TRANSPORT_MISSING: installed local Host transport is missing for ${result.paths.matrixHome}`);
  }
  const hostConfigReload = await reloadRunningHostConfig(result.paths.matrixHome, {
    root: localOwnerTransport.root,
    authorityRoot: localOwnerTransport.authorityRoot ?? localOwnerTransport.root,
    routeKey: null,
    publicNamespace: null,
    spaceId: null,
    forceReload: true,
    label: 'local-owner',
  });
  removeHiveCastCredentialFiles(result.paths);
  return {
    removed: result.removed,
    ...(cloudRevoke ? { cloudRevoke } : {}),
    hostConfigReset: result.hostConfigReset,
    matrixHome: result.paths.matrixHome,
    linkPath: result.paths.linkPath,
    natsCredentialsPath: result.paths.natsCredentialsPath,
    hostConfigReload,
  };
}

export async function hiveCastWhoamiCommand(
  options: IAuthContextOptions = {},
): Promise<IHiveCastWhoamiCommandResult> {
  const cwd = options.cwd ?? process.cwd();
  const { record, paths } = readHiveCastHostLink(cwd, options.home);
  if (!record) {
    return {
      linked: false,
      matrixHome: paths.matrixHome,
    };
  }
  return {
    linked: true,
    matrixHome: paths.matrixHome,
    cloudUrl: record.cloudUrl,
    ...(record.hostLinkId ? { hostLinkId: record.hostLinkId } : {}),
    authorityRoot: record.authorityRoot,
    ...(record.routeKey ? { routeKey: record.routeKey } : {}),
    ...(record.publicNamespace ? { publicNamespace: record.publicNamespace } : {}),
    ...(record.spaceId ? { spaceId: record.spaceId } : {}),
    ...(record.principalId ? { principalId: record.principalId } : {}),
    ...(record.hostName ? { hostName: record.hostName } : {}),
    ...(record.deviceSlug ? { deviceSlug: record.deviceSlug } : {}),
    linkedAt: record.linkedAt,
  };
}

export async function hiveCastLinkStatusCommand(
  options: IAuthContextOptions = {},
): Promise<IHiveCastLinkStatus> {
  const cwd = options.cwd ?? process.cwd();
  return readHiveCastLinkStatus(cwd, options.home);
}

export function formatHiveCastWhoami(result: IHiveCastWhoamiCommandResult): string {
  if (!result.linked) {
    return `HiveCast Host is not linked (${result.matrixHome})`;
  }
  return [
    `Cloud: ${result.cloudUrl ?? 'unknown'}`,
    result.hostName ? `Device name: ${result.hostName}` : null,
    result.deviceSlug ? `Device inventory projection: system.devices.${result.deviceSlug}` : null,
    result.routeKey ? `Route key: ${result.routeKey}` : null,
    result.publicNamespace ? `Public namespace: ${result.publicNamespace}` : null,
    result.spaceId ? `Space: ${result.spaceId}` : null,
    `Authority root: ${result.authorityRoot ?? 'unknown'}`,
    `Matrix home: ${result.matrixHome}`,
  ].filter((line): line is string => Boolean(line)).join('\n');
}
