import type { Paginated } from '@/lib/api/types';
import type { PriceUnit } from '@/services/catalog/types';

/**
 * Search & discovery domain — the family-facing nurse-finding layer. Shapes are derived from the b7
 * contract (`dev/contracts/domains/search.md`) plus the b6 trust badge / b5 variant reads for the
 * profile. 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" + phase §5):
 *  - **Every returned row is already bookable.** The `nurse_search_index` invariant guarantees a hit
 *    only when the nurse is verified + not suspended + accepting + the variant is active. The UI must
 *    **never** re-filter for verification, and never surface an unverified/paused nurse.
 *  - **The result unit is the variant, not the nurse** — a nurse with several variants/areas can appear
 *    as several hits.
 *  - **`districtId = null` ⇒ whole city**, both directions; the client omits `districtId` for a
 *    whole-city search rather than sending a bogus value.
 *  - **Same-gender is first-class** — `nurseGender` is an up-front filter, never silently defaulted or
 *    dropped, and the chosen value is carried into the booking request as `required_caregiver_gender`
 *    (f7), surfaced *before* booking.
 *  - **Money is an IRR digit-string** (`price`) — rendered only via the money util, never parsed to a float.
 *  - **Rating sort only (MVP).**
 *
 * @remarks b7's `NurseSearchResultDto` and the b5/b6 reads do **not** yet expose the nurse's display
 * name, avatar, distance, bio, specialties, full services list, or latest review that C2/C3 render. Those
 * gaps are served by the in-memory mock (`apis/mockApi.ts`, primary this phase) and filed for the backend
 * in `dev/shared-working-context/frontend/requests/for-backend.md`; the real client
 * (`apis/clientApi.ts`) maps what b7/b6/b5 currently provide and is swapped in when the endpoints land.
 */

/** A caregiver's gender — the same-gender matching facet (`any` is expressed by omitting the filter). */
export type NurseGender = 'male' | 'female';

/** The only MVP result ordering. Rendered as a control with one option; other sorts are DEFERRED. */
export type SearchSort = 'rating';

/** The filter object — this **is** the React Query cache key (see `keys.ts`) and the C2 query string. */
export interface NurseSearchFilters {
  serviceCategoryId: number;
  cityId: number;
  /** Omit for a whole-city search; "empty district = whole city" (never send a bogus district). */
  districtId?: number;
  /** Omit = فرقی ندارد / any gender. Never defaulted silently. */
  nurseGender?: NurseGender;
  /** Inclusive IRR-Rial digit-string bounds; compared like-for-like within a `priceUnit`. */
  priceMin?: string;
  priceMax?: string;
  /** Compare only like-for-like listings (e.g. only `per_hour`). */
  priceUnit?: PriceUnit;
  sort: SearchSort;
  page: number;
  pageSize: number;
}

/** A single C2 result card row (one bookable variant matched in a covered area). */
export interface NurseSearchResult {
  nurseId: number;
  variantId: number;
  serviceCategoryId: number;
  /** Display name (mock/future-backend; the real b7 row omits it — card falls back to a label). */
  nurseName: string;
  avatarUrl: string | null;
  /** Always `true` by the search-index invariant — the UI relies on this, never re-checks it. */
  isVerified: boolean;
  averageRating: number;
  totalReviews: number;
  totalCompletedBookings: number;
  /** Kilometres from the searched area; `null` when unknown — the card hides the distance chip. */
  distanceKm: number | null;
  /** The variant's `price` as an IRR-Rial digit-string; rendered via the money util only. */
  priceFromIrr: string;
  priceUnit: PriceUnit;
  nurseGender: NurseGender;
  cityId: number;
  /** `null` = the nurse covers the whole city. */
  districtId: number | null;
}

/** One offered variant on the C3 profile — the bookable unit; reused by the ServicePriceRow. */
export interface NurseProfileServiceRow {
  variantId: number;
  displayName: string;
  /** IRR-Rial digit-string; rendered via the money util + the localized `priceUnit` label. */
  priceIrr: string;
  priceUnit: PriceUnit;
  sessionCount?: number | null;
}

/** A short latest-review snippet for C3 (the full reviews tab is DEFERRED → f13). */
export interface NurseReviewSnippet {
  rating: number;
  body: string;
  /** Author name already masked server-side (PII rule); rendered verbatim. */
  authorMasked: string;
  /** UTC ISO-8601; displayed via the Shamsi date util. */
  createdAt: string;
}

/** The C3 nurse-profile payload. */
export interface NurseProfile {
  nurseId: number;
  nurseName: string;
  avatarUrl: string | null;
  bio: string | null;
  yearsExperience: number | null;
  averageRating: number;
  totalReviews: number;
  totalCompletedBookings: number;
  /** Always `true` for a discoverable nurse (invariant); drives the ✓ تاییدشده badge. */
  isVerified: boolean;
  /** نظام پرستاری (INO membership) — render the badge only when `true`. */
  inoMembership: boolean;
  /** Specialty **codes** (mapped to i18n labels, never rendered raw); the C3 attribute chips. */
  attributeChips: string[];
  services: NurseProfileServiceRow[];
  latestReview?: NurseReviewSnippet | null;
  nurseGender: NurseGender;
}

/**
 * The search domain's API seam — the real HTTP client and the in-memory mock both implement this
 * interface; selection is by config (`USE_SEARCH_MOCK`), never scattered `if (mock)` checks.
 */
export interface SearchApi {
  /** The single family-facing discovery query over the maintained search index. */
  searchNurses(filters: NurseSearchFilters): Promise<Paginated<NurseSearchResult>>;
  /** The C3 nurse profile (identity + badges + services + latest review). */
  getNurseProfile(nurseId: number): Promise<NurseProfile>;
}
