/**
 * Verification domain — the trust engine's front-end data layer. Shapes mirror the b6 contract
 * (`dev/contracts/domains/verification.md`) exactly; the wire is **camelCase** and `clientFetch`
 * unwraps the `ApiResult<T>` envelope, so these are the post-`unwrap()` payloads.
 *
 * Load-bearing semantics (see the contract "Key semantics"):
 *  - `VerificationStatus.status` is the **single source of verification truth**; `isBookable` is the
 *    only flag the UI gates on. The client never infers `is_verified`.
 *  - Steps are **data-driven**: render the ordered `steps[]`, mapping each `code`/`status` to a label
 *    + chip. A new step type appearing in the response must render without a code change.
 *  - Automated steps (`isAutomated:true`) run via a `/run` endpoint; manual steps take a document upload
 *    and wait for an admin decision. **Honest copy** keys off `isAutomated` — a manual step is never
 *    presented as an automated authority check.
 *  - Credential **numbers never cross the wire** (encrypted, never serialized); the badge exposes
 *    credential **types** only.
 */

import type { PageParams, Paginated } from '@/lib/api/types';

/** The aggregate `nurse_verifications.status` — the single source of verification truth. */
export type VerificationAggregateStatus =
  | 'not_started'
  | 'pending'
  | 'in_review'
  | 'approved'
  | 'rejected'
  | 'suspended';

/** Per-step `verification_steps.status`. `failed` renders as the rejected (red) chip; `expired` re-gates. */
export type VerificationStepStatus =
  | 'not_started'
  | 'pending'
  | 'in_review'
  | 'passed'
  | 'failed'
  | 'expired';

/** The six seeded, **stable** step-type codes. Labels are i18n keys off the code — never derived from it. */
export type StepTypeCode =
  | 'identity_kyc'
  | 'shahkar_match'
  | 'moh_competency_license'
  | 'ino_membership'
  | 'criminal_record'
  | 'bank_account_verification';

/** The three credential-bearing step types recorded on admin approval. */
export type CredentialType = 'moh_competency_license' | 'ino_membership' | 'criminal_record';

/** How a credential was verified. Today every real credential resolves `manual` (admin review). */
export type VerificationMethod = 'manual' | 'portal' | 'api';

/**
 * Trust-badge display state — derived client-side, not a wire enum. `verified` when the badge/aggregate
 * is approved; `expired` when a required credential lapsed (distinct from never-verified); else
 * `unverified`. The public badge endpoint only carries `isVerified`, so `expired` is computed by the
 * nurse's own-profile view from its `VerificationStatus`; public consumers (search/f6) see verified/unverified.
 */
export type BadgeState = 'verified' | 'unverified' | 'expired';

/** `VerificationStepDto` — one row of the checklist. Every seeded step is required (Y of the "X از Y" meter). */
export interface VerificationStep {
  id: number;
  code: string;
  /** Server-provided fallback label; the UI prefers the i18n label keyed off `code`. */
  displayName: string;
  status: VerificationStepStatus;
  isAutomated: boolean;
  expiresAt: string | null;
  failureReason: string | null;
}

/** `VerificationStatusDto` — the aggregate + ordered per-step list driving B3/B6. */
export interface VerificationStatus {
  status: VerificationAggregateStatus;
  isBookable: boolean;
  /** Step codes still blocking go-live. */
  blockingSteps: string[];
  steps: VerificationStep[];
}

/** `UploadUrlResult` — a signed PUT target for a manual step's document. */
export interface UploadUrlResult {
  objectStorageKey: string;
  uploadUrl: string;
}

/** `DocumentConfirmedResult` — the step's new status after a document is confirmed. */
export interface DocumentConfirmedResult {
  documentId: number;
  stepStatus: VerificationStepStatus;
}

/** `VerificationDocumentDto` — **metadata only**, never bytes. `url` is a short-lived signed GET URL. */
export interface VerificationDocument {
  id: number;
  contentType: string;
  fileSizeBytes: number;
  originalFileName: string | null;
  url: string;
}

/** `RunStepResult` — the outcome of an automated step run; a vendor fail is `stepStatus:"failed"` + reason. */
export interface RunStepResult {
  stepId: number;
  stepStatus: VerificationStepStatus;
  failureReason: string | null;
}

/** `NurseCredentialDto` — a recorded credential. `credentialNumber` is **never** present. */
export interface NurseCredential {
  id: number;
  credentialType: CredentialType;
  holderNameSnapshot: string;
  issuingAuthority: string;
  issuedAt: string | null;
  expiresAt: string | null;
  verificationMethod: VerificationMethod;
}

/** `TrustBadgeDto` — the public trust signal. Credential **types** only, never numbers. */
export interface TrustBadge {
  nurseId: number;
  isVerified: boolean;
  approvedAt: string | null;
  credentialTypes: string[];
}

/** Body for the automated identity-KYC run. `livenessCaptured` stands in for the vendor liveness payload. */
export interface IdentityKycInput {
  nationalId: string;
  livenessCaptured: boolean;
}

/**
 * Structured professional-credential details the registry needs (INO number, specialties, license
 * number, issuing authority, holder name, issue/expiry). **No nurse-facing b6 endpoint accepts these
 * yet** (admin enters them on review) — the mock persists them and the gap is filed for the backend
 * (`for-backend.md`). The document uploads themselves are contract-backed (upload_url → documents).
 */
export interface CredentialDetailsInput {
  inoNumber: string;
  specialties: string[];
  licenseNumber?: string;
  issuingAuthority?: string;
  holderName?: string;
  issuedAt?: string | null;
  expiresAt?: string | null;
}

/* --- Admin review queue (b6 `AdminVerificationsController`) ---------------------------------------
 * The admin-side surface of the same trust engine. The nurse builds the checklist above; an admin
 * reviews it here — works the queue, opens a case, decides each manual step, and (via a re-aggregate)
 * flips `is_verified`. `credentialNumber` is accepted only as **input** on a decide; it is NEVER on any
 * response DTO (encrypted at rest). Signed document URLs are short-lived — fetched on demand, not cached.
 */

/**
 * `AdminPendingStepDto`, folded to **one row per nurse** — the review queue item. The b6 endpoint returns
 * one row per *step* awaiting attention; the nurse-level aggregate (`stepsPassed`/`stepsTotal`/
 * `nextPendingStepCode`/`hasExpiringCredential`) is the shape the queue UI needs (a nurse-level queue
 * endpoint is filed as REQ-034; the client maps what it can).
 */
export interface AdminVerificationQueueItem {
  nurseVerificationId: number;
  nurseId: number;
  nurseName: string;
  status: VerificationAggregateStatus;
  stepsPassed: number;
  stepsTotal: number;
  nextPendingStepCode: string | null;
  submittedAt: string | null;
  hasExpiringCredential: boolean;
}

/** Queue filter — `status` defaults to `in_review` server-side when omitted. */
export interface AdminVerificationQueueFilters {
  status?: 'pending' | 'in_review';
}

/** `AdminStepDetailDto` — one step of the admin case view, carrying its documents (signed GET URLs). */
export interface AdminVerificationStepDetail {
  id: number;
  code: string;
  displayName: string;
  status: VerificationStepStatus;
  isAutomated: boolean;
  expiresAt: string | null;
  failureReason: string | null;
  documents: VerificationDocument[];
}

/** `AdminVerificationDetailDto` — the full case: ordered steps, recorded credentials, + the identity name for cross-check. */
export interface AdminVerificationCase {
  nurseVerificationId: number;
  nurseId: number;
  identityName: string;
  status: VerificationAggregateStatus;
  steps: AdminVerificationStepDetail[];
  credentials: NurseCredential[];
}

/**
 * Body for `POST admin_verifications/steps/{stepId}/decide`. `rejectionReason` is required when
 * `approve=false`; the credential fields (recorded only on approving a credential-bearing step) include
 * `credentialNumber` — accepted as **input** here, but never echoed back on any response.
 */
export interface DecideStepInput {
  approve: boolean;
  rejectionReason?: string;
  credentialNumber?: string;
  holderName?: string;
  issuingAuthority?: string;
  issuedAt?: string | null;
  expiresAt?: string | null;
  verificationSource?: string;
}

/** `ReviewStepResult` — the step's new status after a decision; `credentialId` is set only when one was recorded. */
export interface DecideStepResult {
  stepId: number;
  stepStatus: VerificationStepStatus;
  credentialId: number | null;
}

/** A freshly-signed, short-lived GET URL for a document — fetched on demand (never long-cached). */
export interface SignedDocumentUrl {
  url: string;
  expiresInSeconds: number;
}

/**
 * The verification domain's API seam — the real HTTP client and the in-memory mock both implement
 * this interface; selection is by config (`USE_VERIFICATION_MOCK`), never scattered `if (mock)` checks.
 */
export interface VerificationApi {
  /** The nurse's own checklist + aggregate + blocking summary (a `not_started` empty list, never a 404). */
  getStatus(): Promise<VerificationStatus>;
  /** Open (or re-open) verification and seed the checklist. Idempotent. */
  start(): Promise<VerificationStatus>;
  /** Automated national-ID + liveness check. A vendor fail is a `failed` status, not a thrown error. */
  runIdentityKyc(input: IdentityKycInput): Promise<RunStepResult>;
  /** Automated phone↔national-id Shahkar match (requires identity KYC passed). Shared-SIM is a handled fail. */
  runShahkarMatch(): Promise<RunStepResult>;
  /** Automated استعلام شبا IBAN-owner ↔ national-id money-mule guard (requires a primary bank account). */
  runBankVerification(): Promise<RunStepResult>;
  /**
   * Upload a document for a manual step (url → PUT bytes → confirm), moving it to `in_review`. Reports
   * upload progress (0–100) via `onProgress`. Returns the server's stored **metadata** (never bytes).
   */
  uploadStepDocument(stepId: number, file: File, onProgress?: (percent: number) => void): Promise<VerificationDocument>;
  /** Persist the structured professional-credential details (gap-filed; mock-persisted for now). */
  submitCredentialDetails(input: CredentialDetailsInput): Promise<void>;
  /** The public trust badge for a nurse (types only). */
  getTrustBadge(nurseId: number): Promise<TrustBadge>;

  // --- Admin review queue (b6 AdminVerificationsController) ---
  /** The review queue, folded to one item per nurse. `status` filters (default `in_review`); paginated. */
  listVerificationQueue(filters: AdminVerificationQueueFilters, params: PageParams): Promise<Paginated<AdminVerificationQueueItem>>;
  /** The full admin case for one nurse — steps + documents + credentials + the identity name for cross-check. */
  getVerificationCase(nurseVerificationId: number): Promise<AdminVerificationCase>;
  /** A freshly-signed, short-lived GET URL for a document — fetched on demand (URLs expire; never long-cached). */
  getDocumentSignedUrl(documentId: number): Promise<SignedDocumentUrl>;
  /** Approve or reject a manual step; on a credential-bearing step, records the (encrypted) credential. Re-aggregates. */
  decideStep(stepId: number, input: DecideStepInput): Promise<DecideStepResult>;
  /** Approve the whole verification (all required steps pass → `approved`), removing it from the queue. */
  approveVerification(nurseVerificationId: number): Promise<void>;
  /** Reject the whole verification (`rejected`), removing it from the queue. */
  rejectVerification(nurseVerificationId: number, reason: string): Promise<void>;
}

/** The specialties offered as ready-made chips in B5 (nurse can add their own). Stable codes → i18n labels. */
export const SPECIALTY_PRESETS: readonly string[] = ['elderly', 'icu', 'pediatric', 'post_surgery', 'wound_care'] as const;

/** Aggregate statuses at which the nurse's services may go live and the trust badge shows verified. */
export function isApproved(status: VerificationStatus | undefined): boolean {
  return status?.status === 'approved' && status.isBookable;
}

/**
 * The trust-badge state for the nurse's **own** profile, computed from the full status: `expired` when a
 * required step has lapsed (distinct from never-verified), `verified` when approved, else `unverified`.
 */
export function ownBadgeState(status: VerificationStatus | undefined): BadgeState {
  if (!status) return 'unverified';
  if (status.steps.some((step) => step.status === 'expired')) return 'expired';
  return isApproved(status) ? 'verified' : 'unverified';
}

/** The public-badge state (search/f6 + public profile) — no `expired` signal on the public payload. */
export function publicBadgeState(badge: TrustBadge | undefined): BadgeState {
  return badge?.isVerified ? 'verified' : 'unverified';
}
