import type {
  AccountId,
  ConnectionId,
  CorrelationId,
  CredentialReferenceId,
  OperationId,
  ProviderId,
} from "./ids.js";
import type { AuthorityContext } from "./security.js";

export type ProviderErrorCategory =
  | "unavailable"
  | "rate-capacity"
  | "auth"
  | "permission"
  | "missing-resource"
  | "unsupported-capability"
  | "conflict-stale"
  | "terminal-provider"
  | "adapter-protocol";

export interface ProviderDescriptor {
  readonly providerId: ProviderId;
  readonly kind: string;
  readonly adapterVersion: string;
  readonly capabilities: readonly string[];
  readonly authModes: readonly string[];
  readonly resourceTypes: readonly string[];
  readonly healthFeatures: readonly string[];
}

export interface ProviderReference {
  readonly providerId: ProviderId;
  readonly accountId?: AccountId;
  readonly resourceType: string;
  readonly nativeId: string;
  readonly nativeRevision?: string;
  readonly url?: string;
  readonly observedAt: string;
}

export interface ProviderOperationContext {
  readonly operationId: OperationId;
  readonly idempotencyKey: string;
  readonly correlationId: CorrelationId;
  readonly authority: AuthorityContext;
  readonly connectionId: ConnectionId;
  readonly credentialReferenceId: CredentialReferenceId;
}

export interface ProviderError {
  readonly category: ProviderErrorCategory;
  readonly retryable: boolean;
  readonly providerId: ProviderId;
  readonly accountId?: AccountId;
  readonly resourceRef?: ProviderReference;
  readonly safeMessage: string;
  readonly observedAt: string;
}

/** Successful provider return envelope. Provider failures are never returned as values. */
export interface ProviderResult<T> {
  readonly value: T;
  readonly references: readonly ProviderReference[];
  readonly observedAt: string;
  readonly reconciliationToken?: string;
}

/**
 * Canonical provider failure transport. Every provider port rejects with this
 * type; adapters must not return ProviderError as a pseudo-success value.
 */
export class ProviderFailure extends Error {
  constructor(readonly providerError: ProviderError) {
    super(providerError.safeMessage);
    this.name = "ProviderFailure";
  }
}

export function isProviderFailure(error: unknown): error is ProviderFailure {
  return error instanceof ProviderFailure;
}

/** A provider call resolves only with ProviderResult and rejects with ProviderFailure. */
export type ProviderCall<T> = Promise<ProviderResult<T>>;
