/**
 * Mosaic Native Kanban — frozen Mechanical Coordinator contracts v1.
 *
 * The pure decision engine and persistence/orchestration service are separate.
 * Neither surface can create scope, edit acceptance, waive gates, certify,
 * merge, release a deployment, or close a provider issue.
 */

import type {
  DeliberateWriteDenialV1,
  InternalKanbanMutationContextV1,
  KanbanEvaluationContextV1,
  KanbanMutationFailureV1,
  RetryableTransportErrorV1,
  VersionConflictV1,
} from './health-state.v1.js';

export const COORDINATOR_CONTRACT_VERSION = '1.0.0' as const;
export type Uuid = string;
export type IsoTimestamp = string;
/** PostgreSQL bigint-safe decimal string; never a JavaScript number. */
export type FencingTokenV1 = string;

export const specialistRoles = [
  'planning',
  'enhance',
  'coder',
  'review',
  'security-review',
  'pr-monitor',
  'certifier',
] as const;
export type SpecialistRole = (typeof specialistRoles)[number];

/** One vocabulary shared with task_assignment_state_v1 in the Drizzle schema. */
export const assignmentStates = [
  'awaiting_approval',
  'policy_pre_authorized',
  'approved',
  'rejected',
  'leased',
  'released',
  'expired',
  'superseded',
] as const;
export type AssignmentStateV1 = (typeof assignmentStates)[number];

export const readinessStates = [
  'dependency-gated',
  'schedule-gated',
  'policy-gated',
  'lease-available',
  'leased',
  'retry-delayed',
  'exhausted',
  'quarantined',
] as const;
export type ReadinessState = (typeof readinessStates)[number];

export interface RetryStateSnapshotV1 {
  disposition: 'available' | 'retry_delayed' | 'quarantined' | 'exhausted';
  attemptCount: number;
  maxAttempts: number;
  nextEligibleAt: IsoTimestamp | null;
  idempotent: boolean;
  terminalReason: string | null;
  version: number;
}

export interface TaskEligibilitySnapshotV1 {
  workspaceId: Uuid;
  taskId: Uuid;
  taskVersion: number;
  projectId: Uuid;
  projectActive: boolean;
  missionId: Uuid | null;
  missionActive: boolean;
  status: 'ready';
  priority: 'critical' | 'high' | 'medium' | 'low';
  boardRank: string;
  dueAt: IsoTimestamp | null;
  notBeforeAt: IsoTimestamp | null;
  createdAt: IsoTimestamp;
  requiredRole: SpecialistRole;
  requiredCapabilities: readonly string[];
  blockingDependencies: readonly {
    taskId: Uuid;
    done: boolean;
    completionConditionSatisfied: boolean;
  }[];
  releaseApproval: {
    decisionId: Uuid;
    approved: boolean;
    policyRevision: string;
  } | null;
  activeLeaseId: Uuid | null;
  retry: RetryStateSnapshotV1;
}

export interface AgentSessionSnapshotV1 {
  workspaceId: Uuid;
  agentId: Uuid;
  sessionId: Uuid;
  state: 'available' | 'busy';
  roles: readonly SpecialistRole[];
  capabilities: readonly string[];
  capacity: number;
  activeLeaseCount: number;
  heartbeatAt: IsoTimestamp;
}

export interface EligibilityExplanationV1 {
  taskId: Uuid;
  eligible: boolean;
  readiness: ReadinessState;
  reasons: readonly {
    gate:
      | 'status'
      | 'project'
      | 'mission'
      | 'dependency'
      | 'schedule'
      | 'retry'
      | 'approval'
      | 'lease'
      | 'capability'
      | 'capacity'
      | 'health';
    satisfied: boolean;
    code: string;
    detail: string;
  }[];
  policyRevision: string;
  evaluatedAt: IsoTimestamp;
}

export interface AssignmentProposalDecisionV1 {
  workspaceId: Uuid;
  taskId: Uuid;
  taskVersion: number;
  targetAgentId: Uuid;
  targetSessionId: Uuid;
  specialistRole: SpecialistRole;
  initialState: 'awaiting_approval' | 'policy_pre_authorized';
  policyRevision: string;
  explanation: EligibilityExplanationV1;
  expiresAt: IsoTimestamp;
}

export interface AssignmentCycleSnapshotV1 {
  context: KanbanEvaluationContextV1;
  tasks: readonly TaskEligibilitySnapshotV1[];
  sessions: readonly AgentSessionSnapshotV1[];
  workspaceFairness: Readonly<Record<Uuid, number>>;
  limit: number;
}

export interface AssignmentCycleDecisionV1 {
  evaluatedTaskCount: number;
  proposals: readonly AssignmentProposalDecisionV1[];
  explanations: readonly EligibilityExplanationV1[];
}

export interface LeaseExpirySnapshotV1 {
  workspaceId: Uuid;
  taskId: Uuid;
  taskVersion: number;
  leaseId: Uuid;
  assignmentId: Uuid;
  sessionId: Uuid;
  fencingToken: FencingTokenV1;
  state: 'pending_ack' | 'active';
  acknowledgeBy: IsoTimestamp;
  expiresAt: IsoTimestamp;
  lastHeartbeatAt: IsoTimestamp | null;
  retry: RetryStateSnapshotV1;
}

export interface LeaseExpiryDecisionV1 {
  leaseId: Uuid;
  action: 'retain' | 'release' | 'retry' | 'quarantine' | 'exhaust';
  reason: string;
  nextEligibleAt: IsoTimestamp | null;
}

/** Pure package owned by KBN-200. It receives complete immutable snapshots. */
export interface MechanicalCoordinatorDecisionEngineV1 {
  evaluateAssignmentCycle(snapshot: AssignmentCycleSnapshotV1): AssignmentCycleDecisionV1;
  explainEligibility(
    context: KanbanEvaluationContextV1,
    task: TaskEligibilitySnapshotV1,
    sessions: readonly AgentSessionSnapshotV1[],
  ): EligibilityExplanationV1;
  decideLeaseExpiry(
    context: KanbanEvaluationContextV1,
    lease: LeaseExpirySnapshotV1,
  ): LeaseExpiryDecisionV1;
}

export interface PersistedAssignmentV1 {
  assignmentId: Uuid;
  workspaceId: Uuid;
  taskId: Uuid;
  taskVersion: number;
  targetAgentId: Uuid;
  targetSessionId: Uuid;
  specialistRole: SpecialistRole;
  state: AssignmentStateV1;
  policyRevision: string;
  proposedBy: { kind: 'user' | 'agent'; id: Uuid };
  reason: string;
  createdAt: IsoTimestamp;
  expiresAt: IsoTimestamp;
}

export interface TaskLeaseV1 {
  leaseId: Uuid;
  workspaceId: Uuid;
  taskId: Uuid;
  taskVersion: number;
  assignmentId: Uuid;
  agentId: Uuid;
  sessionId: Uuid;
  state: 'pending_ack' | 'active';
  fencingToken: FencingTokenV1;
  attempt: number;
  acquiredAt: IsoTimestamp;
  acknowledgeBy: IsoTimestamp;
  lastHeartbeatAt: IsoTimestamp | null;
  expiresAt: IsoTimestamp;
}

interface ServiceCommandBaseV1 {
  context: InternalKanbanMutationContextV1;
  taskId: Uuid;
  expectedTaskVersion: number;
}

export interface AcquireApprovedLeaseCommandV1 extends ServiceCommandBaseV1 {
  assignmentId: Uuid;
  approvalDecisionId: Uuid;
  targetSessionId: Uuid;
  leaseTtlSeconds: number;
}

export interface LeaseCommandV1 extends ServiceCommandBaseV1 {
  leaseId: Uuid;
  sessionId: Uuid;
  fencingToken: FencingTokenV1;
}

export interface HeartbeatLeaseCommandV1 extends LeaseCommandV1 {
  extendSeconds: number;
}

export interface CheckpointCommandV1 extends LeaseCommandV1 {
  sequence: number;
  resumableSummary: string;
  artifactIds: readonly Uuid[];
  contextUsagePercent: number;
}

export interface SubmitForReviewCommandV1 extends LeaseCommandV1 {
  artifactIds: readonly Uuid[];
  summary: string;
}

export interface ReleaseLeaseCommandV1 extends LeaseCommandV1 {
  reason:
    | 'worker_requested'
    | 'ack_timeout'
    | 'heartbeat_timeout'
    | 'task_submitted'
    | 'policy_revoked'
    | 'shutdown';
}

export interface AssignmentCycleCommandV1 {
  context: InternalKanbanMutationContextV1;
  limit: number;
}

export interface ExpirySweepCommandV1 {
  context: InternalKanbanMutationContextV1;
  limit: number;
}

export interface RecoverCoordinatorCommandV1 {
  context: InternalKanbanMutationContextV1;
}

interface CoordinatorRejectionBaseV1 {
  kind: 'coordinator_rejection';
  retryable: false;
  requestOutcome: 'not_applied';
  correlationId: Uuid;
  idempotencyKey: string;
  message: string;
}

export type CoordinatorPolicyRejectionV1 =
  | (CoordinatorRejectionBaseV1 & { code: 'WORKSPACE_MISMATCH' })
  | (CoordinatorRejectionBaseV1 & {
      code: 'TASK_NOT_ELIGIBLE';
      explanation: EligibilityExplanationV1;
    })
  | (CoordinatorRejectionBaseV1 & { code: 'APPROVAL_REQUIRED' })
  | (CoordinatorRejectionBaseV1 & { code: 'APPROVAL_STALE' })
  | (CoordinatorRejectionBaseV1 & { code: 'ASSIGNMENT_STALE' })
  | (CoordinatorRejectionBaseV1 & { code: 'ASSIGNMENT_TARGET_MISMATCH' })
  | (CoordinatorRejectionBaseV1 & { code: 'POLICY_REVISION_MISMATCH' })
  | (CoordinatorRejectionBaseV1 & { code: 'ARTIFACT_WORKSPACE_MISMATCH' })
  | (CoordinatorRejectionBaseV1 & { code: 'LEASE_ALREADY_ACTIVE' })
  | (CoordinatorRejectionBaseV1 & { code: 'LEASE_NOT_FOUND' })
  | (CoordinatorRejectionBaseV1 & { code: 'LEASE_NOT_ACTIVE' })
  | (CoordinatorRejectionBaseV1 & {
      code: 'ACK_DEADLINE_EXPIRED';
      expiredAt: IsoTimestamp;
    })
  | (CoordinatorRejectionBaseV1 & {
      code: 'FENCING_TOKEN_STALE';
      currentFencingToken: FencingTokenV1;
    })
  | (CoordinatorRejectionBaseV1 & { code: 'SESSION_MISMATCH' })
  | (CoordinatorRejectionBaseV1 & {
      code: 'HEARTBEAT_EXPIRED';
      expiredAt: IsoTimestamp;
    })
  | (CoordinatorRejectionBaseV1 & {
      code: 'CHECKPOINT_SEQUENCE_CONFLICT';
      currentSequence: number;
    })
  | (CoordinatorRejectionBaseV1 & { code: 'RETRY_EXHAUSTED' })
  | (CoordinatorRejectionBaseV1 & {
      code: 'NON_IDEMPOTENT_RETRY_REQUIRES_ORCHESTRATOR';
    });

/** Explicit mapping to the Gateway mutation failure union; no arbitrary booleans. */
export type CoordinatorFailureV1 =
  | DeliberateWriteDenialV1
  | VersionConflictV1
  | RetryableTransportErrorV1
  | CoordinatorPolicyRejectionV1;

export interface CoordinatorSuccessV1<T> {
  ok: true;
  value: T;
  correlationId: Uuid;
}
export interface CoordinatorFailureResultV1 {
  ok: false;
  failure: CoordinatorFailureV1;
}
export type CoordinatorResultV1<T> = CoordinatorSuccessV1<T> | CoordinatorFailureResultV1;

export interface ExpirySweepResultV1 {
  examined: number;
  released: readonly Uuid[];
  retryScheduled: readonly Uuid[];
  quarantined: readonly Uuid[];
  exhausted: readonly Uuid[];
}

export interface RestartRecoveryResultV1 {
  activeLeaseIds: readonly Uuid[];
  expiredLeaseIds: readonly Uuid[];
  pendingAssignmentIds: readonly Uuid[];
  pendingOutboxEventIds: readonly Uuid[];
}

/** Persistence/Gateway adapter owned by KBN-210. */
export interface MechanicalCoordinatorServicePortV1 {
  /** Loads immutable snapshots, invokes pure engine, and persists proposals atomically. */
  runAssignmentCycle(
    command: AssignmentCycleCommandV1,
  ): Promise<CoordinatorResultV1<{ assignments: readonly PersistedAssignmentV1[] }>>;

  /** Query path loads by ID; public health observation cannot authorize mutation. */
  getEligibilityExplanation(
    context: KanbanEvaluationContextV1,
    taskId: Uuid,
  ): Promise<CoordinatorResultV1<EligibilityExplanationV1>>;

  /**
   * Accepts IDs only. Implementation reloads and locks assignment + approval +
   * task + target session in PostgreSQL, then verifies workspace, task version,
   * target agent/session, state, expiry, policy revision, and current approval.
   */
  acquireApprovedLease(
    command: AcquireApprovedLeaseCommandV1,
  ): Promise<CoordinatorResultV1<TaskLeaseV1>>;

  acknowledgeLease(command: LeaseCommandV1): Promise<CoordinatorResultV1<TaskLeaseV1>>;
  heartbeatLease(command: HeartbeatLeaseCommandV1): Promise<CoordinatorResultV1<TaskLeaseV1>>;
  appendCheckpoint(
    command: CheckpointCommandV1,
  ): Promise<CoordinatorResultV1<{ checkpointId: Uuid }>>;
  submitForReview(
    command: SubmitForReviewCommandV1,
  ): Promise<CoordinatorResultV1<{ taskVersion: number; status: 'in_review' }>>;
  releaseLease(command: ReleaseLeaseCommandV1): Promise<CoordinatorResultV1<{ released: true }>>;
  expireAndRecover(
    command: ExpirySweepCommandV1,
  ): Promise<CoordinatorResultV1<ExpirySweepResultV1>>;
  recoverFromPostgres(
    command: RecoverCoordinatorCommandV1,
  ): Promise<CoordinatorResultV1<RestartRecoveryResultV1>>;
}

/** Compile-time mapping guarantee: Coordinator Gateway failures are Kanban failures or exact policy rejections. */
export function isKanbanMutationFailureV1(
  failure: CoordinatorFailureV1,
): failure is KanbanMutationFailureV1 {
  return (
    failure.kind === 'deliberate_fail_closed_denial' ||
    failure.kind === 'retryable_transport_error' ||
    failure.kind === 'version_conflict'
  );
}
