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

/**
 * Bookings domain — the **post-payment engagement** layer of the lifecycle (b9). An
 * `accepted_awaiting_payment` request that is paid converts (`bookings/convert`) into a `bookings` row
 * with N `booking_sessions`, an encrypted `booking_care_instructions`, and per-session EVV
 * (`visit_verifications`). This is the sibling of the **pre-payment** `services/bookingRequests` domain
 * (b8) — a distinct contract (`dev/contracts/domains/bookings-evv.md`), distinct routes
 * (`/api/v1/bookings/*` + `/api/v1/booking_sessions/*`), distinct shapes — **not** a rename of it.
 *
 * Shapes mirror the b9 swagger 1:1 (camelCase; `clientFetch` unwraps the `ApiEnvelope<T>`, so these are
 * the post-`unwrap()` payloads). Load-bearing semantics (contract + phase §5):
 *  - **Money is display-only and never computed.** `grossPriceIrr = balinyaarCommissionIrr +
 *    nursePayoutAmount` is guaranteed server-side; render the three IRR **digit-strings** as-is through
 *    the money util — never sum, re-split, or derive them client-side.
 *  - **The status timeline is server truth.** `BookingDetailDto.status` is the single source; never
 *    advance/infer a step client-side. After an EVV mutation, invalidate and re-render from the server.
 *  - **Two-stage clinical disclosure.** `CareInstructionsDto` is decrypted and returned **only** to the
 *    assigned nurse (or admin) on a `confirmed`+ booking; the client must not even request it otherwise.
 *  - **EVV mismatch is advisory, never a block.** `checkInAddressMatch` (`true` in range · `false`
 *    out-of-range/under-review · `null` GPS unavailable) drives a banner, never a gate.
 *  - **Payout-eligibility is server truth.** `payoutEligibleAt` is gated by the dispute window
 *    server-side; render it, never recompute it.
 */

/** `BookingStatus` — the seven-state booking lifecycle (contract enum, stable string codes). */
export type BookingStatus =
  | 'pending_payment'
  | 'confirmed'
  | 'in_progress'
  | 'completed'
  | 'disputed'
  | 'closed'
  | 'cancelled';

/** `BookingSessionStatus` — per-visit lifecycle (contract enum). */
export type BookingSessionStatus = 'scheduled' | 'in_progress' | 'completed' | 'missed' | 'cancelled';

/** `VisitVerificationStatus` — the EVV state of a session (contract enum). */
export type VisitVerificationStatus = 'pending' | 'checked_in' | 'completed';

/** Which "my bookings" scope to read — `all` is admin-only server-side (contract `list?role=`). */
export type BookingListRole = 'customer' | 'nurse' | 'all';

/**
 * The viewer's actor role for a booking-detail read. Drives (a) the mock's address-snapshot masking
 * (the nurse view omits it) and (b) the client-side care-instructions **UI gate**. The real server
 * infers the view from auth + tenancy; this is passed for the mock and the gate. Admin is out of scope
 * this phase (its console is f15).
 */
export type BookingViewerRole = 'customer' | 'nurse';

/**
 * The happy-path timeline order rendered by `BookingStatusTimeline`. Terminal branches
 * (`disputed`/`closed`/`cancelled`) are shown distinctly, off this line — see `isBookingTerminalBranch`.
 */
export const BOOKING_TIMELINE_ORDER: readonly BookingStatus[] = [
  'pending_payment',
  'confirmed',
  'in_progress',
  'completed',
] as const;

/** `disputed`/`closed`/`cancelled` leave the happy path — rendered as a distinct terminal state. */
export function isBookingTerminalBranch(status: BookingStatus): boolean {
  return status === 'disputed' || status === 'closed' || status === 'cancelled';
}

/**
 * `confirmed` or beyond — the booking has been paid/converted. Gates the care-instructions read
 * (two-stage disclosure) and the "upcoming sessions" content. `pending_payment` and `cancelled` are
 * **not** confirmed+; `disputed`/`closed`/`completed` are (they follow confirmation).
 */
export function isBookingConfirmedOrBeyond(status: BookingStatus): boolean {
  return (
    status === 'confirmed' ||
    status === 'in_progress' ||
    status === 'completed' ||
    status === 'disputed' ||
    status === 'closed'
  );
}

/**
 * The active step index for the 4-step timeline. A terminal branch reports the step it left from
 * (`cancelled` from wherever, rendered distinctly), so callers should check `isBookingTerminalBranch`
 * first and only use this for the happy path.
 */
export function bookingTimelineActiveIndex(status: BookingStatus): number {
  const idx = BOOKING_TIMELINE_ORDER.indexOf(status);
  if (idx >= 0) return idx;
  // disputed/closed follow completion → sit the line at "completed"; cancelled is rendered distinctly
  // (off the line) by the timeline, so its index is only a harmless fallback.
  if (status === 'disputed' || status === 'closed') return BOOKING_TIMELINE_ORDER.indexOf('completed');
  return BOOKING_TIMELINE_ORDER.indexOf('confirmed');
}

/** A per-visit session (embedded in `BookingDetailDto.sessions`; `BookingSessionSummaryDto` on the wire). */
export interface BookingSessionDto {
  id: number;
  sessionIndex: number;
  /** ISO date `YYYY-MM-DD`. */
  scheduledDate: string;
  /** `HH:mm:ss`. */
  scheduledTimeStart: string;
  scheduledTimeEnd: string;
  status: BookingSessionStatus;
  /** IRR digit-string — this session's share of the nurse payout (Σ over sessions = nursePayoutAmount). */
  visitPayoutAmount: string;
  /** Server truth: set at check-out, gated by the dispute window. `null` until eligible. Never computed. */
  payoutEligibleAt: string | null;
  evvStatus: VisitVerificationStatus;
  /** Server `checked_in_at` — the banner renders its Shamsi/clock from this, never a client clock. */
  checkInAt: string | null;
  checkOutAt: string | null;
  /** Advisory: `true` in range · `false` out-of-range (under review) · `null` GPS unavailable. */
  checkInAddressMatch: boolean | null;
}

/** The booking header + money summary + embedded sessions (`bookings/get`, `convert`, `transition`). */
export interface BookingDetailDto {
  id: number;
  bookingRequestId: number;
  status: BookingStatus;
  nurseId: number;
  nurseName: string;
  patientId: number;
  patientName: string;
  variantId: number;
  variantSnapshotJson: string;
  customerAddressId: number;
  /** Full snapshot for the customer/admin; **`null` in the nurse view** (masked server-side). */
  addressSnapshotJson: string | null;
  /** The three money amounts — IRR digit-strings; `gross = commission + payout`, guaranteed server-side. */
  grossPriceIrr: string;
  balinyaarCommissionIrr: string;
  nursePayoutAmount: string;
  /** PSP fee (nullable) — a checkout concern, surfaced here for completeness. */
  pspFeeAmount: string | null;
  /** Snapshotted commission rate (decimal). */
  platformFeeRate: number;
  sessionCount: number;
  scheduledDate: string;
  scheduledTimeStart: string;
  scheduledTimeEnd: string;
  confirmedAt: string | null;
  completedAt: string | null;
  cancelledAt: string | null;
  cancelledBy: string | null;
  cancellationReason: string | null;
  cancellationPolicyCode: string | null;
  cancellationRefundPercentage: number | null;
  refundableAmountIrr: string | null;
  /** Set when the booking completes; the payout gate reads from this. `null` before completion. */
  disputeWindowEndsAt: string | null;
  createdAt: string;
  sessions: BookingSessionDto[];
}

/** A row in the role-scoped "My bookings" list (`bookings/list`). `amountIrr` = gross (customer) / payout (nurse). */
export interface BookingListItemDto {
  id: number;
  status: BookingStatus;
  counterpartyName: string;
  scheduledDate: string;
  sessionCount: number;
  amountIrr: string;
  disputeWindowEndsAt: string | null;
  createdAt: string;
}

/** A row in the nurse's "today" session feed (`booking_sessions/today`) with EVV CTA state. */
export interface BookingSessionListItemDto {
  sessionId: number;
  bookingId: number;
  sessionIndex: number;
  patientName: string;
  scheduledDate: string;
  scheduledTimeStart: string;
  scheduledTimeEnd: string;
  status: BookingSessionStatus;
  evvStatus: VisitVerificationStatus;
}

/**
 * Per-session EVV detail (`booking_sessions/evv/{id}`). Raw GPS (`*Lat`/`*Lng`/`checkInDistanceMeters`)
 * is gated to the owning nurse + admin server-side. The banner needs only `checkInAt` +
 * `checkInAddressMatch`; the coordinates are informational.
 */
export interface VisitVerificationDto {
  id: number;
  bookingSessionId: number;
  status: VisitVerificationStatus;
  checkInAt: string | null;
  checkInLat: number | null;
  checkInLng: number | null;
  checkOutAt: string | null;
  checkOutLat: number | null;
  checkOutLng: number | null;
  checkInAddressMatch: boolean | null;
  checkInDistanceMeters: number | null;
}

/**
 * The decrypted stage-2 clinical/logistical context (`bookings/care_instructions/{id}`). Encrypted at
 * rest; **present only in the gated read** to the assigned nurse (or admin) post-confirmation. All fields
 * are free-text and nullable (the write path is `bookings/submit_care_instructions/{id}`, customer/admin).
 */
export interface CareInstructionsDto {
  bookingId: number;
  currentConditions: string | null;
  medications: string | null;
  allergies: string | null;
  specialInstructions: string | null;
  emergencyContactName: string | null;
  emergencyContactPhone: string | null;
}

/**
 * EVV check-in command. `latitude`/`longitude` are **nullable** — a GPS-denied nurse still checks in
 * (flagged, never blocked). `capturedAt` is the client capture instant; the server timestamps the
 * authoritative `checkInAt`, so the real client sends only the coordinates (the contract command carries
 * `latitude`/`longitude`/`sessionId`). Kept on the input for the mock's banner + audit fidelity.
 */
export interface CheckInVisitInput {
  bookingSessionId: number;
  latitude: number | null;
  longitude: number | null;
  /** ISO instant the client captured position; server time is authoritative. */
  capturedAt: string;
}

/** EVV check-out command — same shape; must follow an open check-in (a `400` otherwise). */
export type CheckOutVisitInput = CheckInVisitInput;

/** `bookings/list` query params (role-scoped, paginated, optional status filter). */
export interface BookingListParams extends PageParams {
  role: BookingListRole;
  status?: BookingStatus;
}

/** `booking_sessions/today` query params (a nurse's day; default = all today). */
export interface TodaySessionsParams extends PageParams {
  /** ISO date `YYYY-MM-DD`; omitted = the server's "today". */
  date?: string;
}

/**
 * The bookings API seam — the real HTTP client and the in-memory mock both implement this interface;
 * selection is by config (`USE_BOOKINGS_MOCK`), never scattered `if (mock)` checks.
 *
 * `getBookingDetail`/`getCareInstructions` take an optional `viewerRole` that only the mock uses (address
 * masking + the care-instructions 404 boundary); the real client infers the view from auth and ignores it.
 */
export interface BookingsApi {
  getBookingDetail(id: number, viewerRole?: BookingViewerRole): Promise<BookingDetailDto>;
  listBookings(params: BookingListParams): Promise<Paginated<BookingListItemDto>>;
  listTodaySessions(params: TodaySessionsParams): Promise<Paginated<BookingSessionListItemDto>>;
  getSessionEvv(sessionId: number): Promise<VisitVerificationDto>;
  getCareInstructions(bookingId: number, viewerRole?: BookingViewerRole): Promise<CareInstructionsDto>;
  checkInVisit(input: CheckInVisitInput): Promise<VisitVerificationDto>;
  checkOutVisit(input: CheckOutVisitInput): Promise<VisitVerificationDto>;
}
