import type {
  AgentRunId,
  AttemptId,
  ChangeSetId,
  ConnectionId,
  CredentialMutationAuthority,
  CredentialReference,
  CredentialReferenceId,
  FactoryRunId,
  OperationId,
  PlanRevisionId,
  ProviderDescriptor,
  ProviderOperationContext,
  ProviderReference,
  ProviderCall,
  TaskId,
  WorkspaceId,
} from "@awp/contracts";
import type { CandidateManifest } from "@awp/domain";

export interface ReconcileRequest {
  readonly context: ProviderOperationContext;
  readonly reference: ProviderReference;
}
export interface ProviderObservation {
  readonly state: string;
  readonly details: Readonly<Record<string, unknown>>;
}
export interface DescribedProvider {
  describe(): Promise<ProviderDescriptor>;
}

export interface AccountDescriptor {
  readonly accountKey: string;
  readonly label: string;
  readonly capabilities: readonly string[];
}
export interface AccountProvider extends DescribedProvider {
  listAccounts(context: ProviderOperationContext): ProviderCall<readonly AccountDescriptor[]>;
}

export interface RepositoryInspection {
  readonly defaultBranch: string;
  readonly headRevision: string;
}
export interface ForgeProvider extends DescribedProvider {
  inspectRepository(
    context: ProviderOperationContext,
    repositoryKey: string,
  ): ProviderCall<RepositoryInspection>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}

export interface TrustedPublishRequest {
  readonly context: ProviderOperationContext;
  readonly changeSetId: ChangeSetId;
  readonly factoryRunId: FactoryRunId;
  readonly repositoryKey: string;
  readonly baseRevision: string;
  readonly candidateDigest: string;
  readonly candidateManifest: CandidateManifest;
  readonly diff: string;
}
export interface TrustedPublisher {
  publish(request: TrustedPublishRequest): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}

export interface RepositoryRevisionIdentity {
  readonly reference: string;
  readonly revision: string;
}
export interface TrustedMergeRequest {
  readonly context: ProviderOperationContext;
  readonly changeSetId: ChangeSetId;
  readonly repositoryKey: string;
  readonly publicationReference: ProviderReference;
  readonly candidateDigest: string;
  readonly candidateManifest: CandidateManifest;
  readonly expectedBase: RepositoryRevisionIdentity;
  readonly expectedTarget: RepositoryRevisionIdentity;
}
export interface TrustedMergeResult {
  readonly changeSetId: ChangeSetId;
  readonly resultingRevision: string;
  readonly mergeReference: ProviderReference;
}
export type TrustedMergeReconciliation =
  | { readonly state: "merged"; readonly result: TrustedMergeResult }
  | { readonly state: "not-merged" };
export interface TrustedMerger {
  merge(request: TrustedMergeRequest): ProviderCall<TrustedMergeResult>;
  reconcile(request: TrustedMergeRequest): ProviderCall<TrustedMergeReconciliation>;
}

export interface FactoryProvider extends DescribedProvider {
  start(
    context: ProviderOperationContext,
    factoryRunId: FactoryRunId,
    planRevisionId: PlanRevisionId,
  ): ProviderCall<ProviderObservation>;
  cancel(
    context: ProviderOperationContext,
    factoryRunId: FactoryRunId,
  ): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}
export interface AgentProvider extends DescribedProvider {
  startAttempt(
    context: ProviderOperationContext,
    attemptId: AttemptId,
    agentRunId: AgentRunId,
    taskId: TaskId,
    workspaceId: WorkspaceId,
  ): ProviderCall<ProviderObservation>;
  cancelAttempt(
    context: ProviderOperationContext,
    attemptId: AttemptId,
  ): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}
export interface DurableWorkflowProvider extends DescribedProvider {
  start(
    context: ProviderOperationContext,
    operationId: OperationId,
    workflowKind: string,
    input: unknown,
  ): ProviderCall<ProviderObservation>;
  cancel(
    context: ProviderOperationContext,
    operationId: OperationId,
  ): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}
export interface WorkspaceLaunchSpec {
  readonly environment: Readonly<Record<string, string>>;
}
export interface WorkspaceProvider extends DescribedProvider {
  create(
    context: ProviderOperationContext,
    workspaceId: WorkspaceId,
    profileKey: string,
    agentRunId?: AgentRunId,
    launch?: WorkspaceLaunchSpec,
  ): ProviderCall<ProviderObservation>;
  replaceCompute?(
    context: ProviderOperationContext,
    workspaceId: WorkspaceId,
    profileKey: string,
    agentRunId?: AgentRunId,
    launch?: WorkspaceLaunchSpec,
  ): ProviderCall<ProviderObservation>;
  attestCheckpoint?(
    context: ProviderOperationContext,
    workspaceId: WorkspaceId,
    profileKey: string,
    digest: string,
    observedAt: string,
  ): ProviderCall<ProviderObservation>;
  cleanupCheckpoint?(
    context: ProviderOperationContext,
    workspaceId: WorkspaceId,
    profileKey: string,
    expectedDigest: string,
    collected: boolean,
  ): Promise<void>;
  destroy(
    context: ProviderOperationContext,
    workspaceId: WorkspaceId,
  ): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}
export type RequiredCheckState = "passed" | "failed" | "missing";
export interface RequiredCheckObservation {
  readonly name: string;
  readonly state: RequiredCheckState;
  readonly observedAt: string;
  readonly details?: Readonly<Record<string, unknown>>;
}
export interface RequiredCheckSnapshot {
  readonly repositoryKey: string;
  readonly branch: string;
  readonly revision: string;
  readonly requiredChecks: readonly string[];
  readonly checks: readonly RequiredCheckObservation[];
}
export interface RequiredChecksProvider extends DescribedProvider {
  observeRequiredChecks(
    context: ProviderOperationContext,
    repositoryKey: string,
    branch: string,
    revision: string,
  ): ProviderCall<RequiredCheckSnapshot>;
}

export interface CIProvider extends DescribedProvider {
  start(
    context: ProviderOperationContext,
    repositoryKey: string,
    candidateDigest: string,
  ): ProviderCall<ProviderObservation>;
  cancel(
    context: ProviderOperationContext,
    reference: ProviderReference,
  ): ProviderCall<ProviderObservation>;
  reconcile(request: ReconcileRequest): ProviderCall<ProviderObservation>;
}

export interface SecretLease {
  readonly value: string;
  readonly expiresAt?: string;
  dispose(): void;
}
export interface SecretStore {
  resolve(reference: CredentialReference, purpose: string): Promise<SecretLease>;
  rotate(
    referenceId: CredentialReferenceId,
    connectionId: ConnectionId,
    authority: CredentialMutationAuthority,
  ): Promise<CredentialReference>;
}
