export interface HiveCastProviderConfig {
  readonly cloud: string;
  readonly apiKey: string;
  readonly space: string;
  readonly profile?: string;
  readonly fetch?: typeof fetch;
}

export interface HiveCastActorCredentialRequest {
  readonly mount: string;
  readonly actorId?: string;
  readonly packageId?: string;
  readonly runtimeId?: string;
  readonly accepts?: Record<string, unknown>;
  readonly emits?: Record<string, unknown>;
  readonly subscribes?: Record<string, unknown>;
  readonly ttlSeconds?: number;
}

export interface HiveCastCredentialPair {
  readonly jwt: string;
  readonly seed: string;
  readonly userPublicKey?: string;
  readonly expiresAt?: string;
}

export interface HiveCastTransportInfo {
  readonly root: string;
  readonly natsUrl?: string;
  readonly natsWsUrl?: string;
  readonly leafnodeUrl?: string;
}

export interface HiveCastActorCredential {
  readonly ok: true;
  readonly cloud: string;
  readonly space: string;
  readonly root: string;
  readonly mount: string;
  readonly effectiveMount?: string;
  readonly transport: HiveCastTransportInfo;
  readonly credentials: HiveCastCredentialPair;
  readonly key?: Record<string, unknown>;
  readonly source?: string;
  readonly credentialPipeline?: string;
}

export interface HiveCastKeyInfo {
  readonly ok: true;
  readonly cloud: string;
  readonly space: string;
  readonly key?: Record<string, unknown>;
  readonly principal?: Record<string, unknown>;
  readonly profile?: Record<string, unknown>;
}

function requiredString(value: unknown, label: string): string {
  if (typeof value !== 'string' || !value.trim()) {
    throw new Error(`HiveCast response missing ${label}`);
  }
  return value.trim();
}

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

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

function normalizeCloud(cloud: string): string {
  const normalized = cloud.trim().replace(/\/+$/g, '');
  if (!normalized) throw new Error('HiveCast cloud URL is required');
  return normalized;
}

async function postHiveCastJson(
  config: HiveCastProviderConfig,
  path: string,
  body: Record<string, unknown>,
): Promise<Record<string, unknown>> {
  const cloud = normalizeCloud(config.cloud);
  const fetchImpl = config.fetch ?? globalThis.fetch;
  if (typeof fetchImpl !== 'function') {
    throw new Error('HiveCast provider requires fetch');
  }
  const response = await fetchImpl(`${cloud}${path}`, {
    method: 'POST',
    headers: {
      authorization: `Bearer ${config.apiKey}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify(body),
  });
  const text = await response.text();
  const parsed = text.trim() ? record(JSON.parse(text)) : {};
  if (!response.ok || parsed.ok !== true) {
    const error = optionalString(parsed.error) ?? `HTTP_${response.status}`;
    throw new Error(`HiveCast ${path} failed: ${error}`);
  }
  return parsed;
}

export async function validateHiveCastApiKey(config: HiveCastProviderConfig): Promise<HiveCastKeyInfo> {
  const cloud = normalizeCloud(config.cloud);
  const body = await postHiveCastJson(config, '/api/install/key-info', {
    space: config.space,
    ...(config.profile ? { profile: config.profile } : {}),
  });
  return {
    ok: true,
    cloud,
    space: config.space,
    key: record(body.key),
    principal: record(body.principal),
    profile: record(body.profile),
  };
}

export async function exchangeHiveCastActorCredential(
  config: HiveCastProviderConfig,
  request: HiveCastActorCredentialRequest,
): Promise<HiveCastActorCredential> {
  const cloud = normalizeCloud(config.cloud);
  if (!config.apiKey.trim()) throw new Error('HiveCast API key is required');
  if (!config.space.trim()) throw new Error('HiveCast space is required');
  if (!request.mount.trim()) throw new Error('HiveCast actor credential exchange requires mount');

  const body = await postHiveCastJson(config, '/api/install/actor-credential', {
    space: config.space,
    mount: request.mount,
    ...(config.profile ? { profile: config.profile } : {}),
    ...(request.actorId ? { actorId: request.actorId } : {}),
    ...(request.packageId ? { packageId: request.packageId } : {}),
    ...(request.runtimeId ? { runtimeId: request.runtimeId } : {}),
    ...(request.accepts ? { accepts: request.accepts } : {}),
    ...(request.emits ? { emits: request.emits } : {}),
    ...(request.subscribes ? { subscribes: request.subscribes } : {}),
    ...(request.ttlSeconds ? { ttlSeconds: request.ttlSeconds } : {}),
  });

  const transport = record(body.transport);
  const credentials = record(body.credentials);
  const space = record(body.space);
  const root = optionalString(transport.root)
    ?? optionalString(space.authorityRoot)
    ?? optionalString(body.authorityRoot)
    ?? optionalString(body.root)
    ?? config.space;

  return {
    ok: true,
    cloud,
    space: config.space,
    root,
    mount: request.mount,
    effectiveMount: optionalString(body.effectiveMount),
    transport: {
      root,
      natsUrl: optionalString(transport.natsUrl),
      natsWsUrl: optionalString(transport.natsWsUrl),
      leafnodeUrl: optionalString(transport.leafnodeUrl),
    },
    credentials: {
      jwt: requiredString(credentials.jwt, 'credentials.jwt'),
      seed: requiredString(credentials.seed, 'credentials.seed'),
      userPublicKey: optionalString(credentials.userPublicKey),
      expiresAt: optionalString(credentials.expiresAt),
    },
    key: record(body.key),
    source: optionalString(body.source),
    credentialPipeline: optionalString(body.credentialPipeline),
  };
}
