import { clientFetch } from '@/lib/api/client';
import { unwrap, type ApiEnvelope, type Paginated } from '@/lib/api/types';
import type { PriceUnit } from '@/services/catalog/types';
import { SEARCH_PAGE_SIZE } from '../constants';
import type {
  NurseGender,
  NurseProfile,
  NurseProfileServiceRow,
  NurseReviewSnippet,
  NurseSearchFilters,
  NurseSearchResult,
  SearchApi,
} from '../types';

const SEARCH_BASE = '/api/v1/search';
const NURSES_BASE = '/api/v1/nurses';

/** The b7 `NurseSearchResultDto` (the projected index row) — the exact wire shape we map from. */
interface NurseSearchResultDto {
  variantId: number;
  nurseId: number;
  serviceCategoryId: number;
  price: string;
  priceUnit: PriceUnit;
  nurseGender: NurseGender;
  averageRating: number;
  totalReviews: number;
  totalCompletedBookings: number;
  cityId: number;
  districtId: number | null;
  /** REQ-012 — the C2 card identity, now denormalized into the index row. */
  nurseName: string | null;
  avatarUrl: string | null;
  distanceKm: number | null;
}

/** The b6/b7 aggregated `NursePublicProfileDto` (REQ-012) — the C3 profile payload. */
interface NursePublicProfileDto {
  nurseId: number;
  nurseName: string;
  avatarUrl: string | null;
  bio: string;
  yearsExperience: number;
  averageRating: number;
  totalReviews: number;
  totalCompletedBookings: number;
  isVerified: boolean;
  inoMembership: boolean;
  attributeChips: string[];
  services: {
    variantId: number;
    displayName: string;
    priceIrr: string;
    priceUnit: PriceUnit;
    sessionCount: number | null;
  }[];
  latestReview: { rating: number; body: string | null; authorMasked: string | null; createdAt: string } | null;
}

/**
 * Real HTTP implementation of the `SearchApi` seam (b7 `search/nurses`, b6/b7 public profile). Routes are
 * action-style + snake_case; query params are snake_case per the contract; JSON fields are camelCase and
 * `clientFetch` returns the raw envelope, so we `unwrap()`.
 *
 * The PRIMARY implementation once `USE_SEARCH_MOCK = false` (refinement-phase-4). REQ-012 delivered the
 * discovery enrichment the C2 card + C3 profile need: `nurseName`/`avatarUrl`/`distanceKm` are now
 * denormalized onto `NurseSearchResultDto`, and `GET nurses/{id}/profile` aggregates identity + bio +
 * specialties + the full services list + the latest review. This client maps both 1:1.
 */
export const searchClientApi: SearchApi = {
  searchNurses: async (filters: NurseSearchFilters): Promise<Paginated<NurseSearchResult>> => {
    const query = new URLSearchParams();
    query.set('service_category_id', String(filters.serviceCategoryId));
    query.set('city_id', String(filters.cityId));
    if (filters.districtId != null) query.set('district_id', String(filters.districtId));
    if (filters.nurseGender) query.set('nurse_gender', filters.nurseGender);
    if (filters.priceMin) query.set('min_price', filters.priceMin);
    if (filters.priceMax) query.set('max_price', filters.priceMax);
    if (filters.priceUnit) query.set('price_unit', filters.priceUnit);
    query.set('page', String(filters.page || 1));
    query.set('page_size', String(filters.pageSize || SEARCH_PAGE_SIZE));

    const paged = unwrap(
      await clientFetch<ApiEnvelope<Paginated<NurseSearchResultDto>>>(
        `${SEARCH_BASE}/nurses?${query.toString()}`,
      ),
    );

    return {
      ...paged,
      items: paged.items.map((dto) => ({
        nurseId: dto.nurseId,
        variantId: dto.variantId,
        serviceCategoryId: dto.serviceCategoryId,
        // REQ-012 — identity denormalized onto the index row; card falls back to a label only when null.
        nurseName: dto.nurseName ?? '',
        avatarUrl: dto.avatarUrl,
        // Every returned row is searchable by the index invariant.
        isVerified: true,
        averageRating: dto.averageRating,
        totalReviews: dto.totalReviews,
        totalCompletedBookings: dto.totalCompletedBookings,
        distanceKm: dto.distanceKm,
        priceFromIrr: dto.price,
        priceUnit: dto.priceUnit,
        nurseGender: dto.nurseGender,
        cityId: dto.cityId,
        districtId: dto.districtId,
      })),
    };
  },

  getNurseProfile: async (nurseId: number): Promise<NurseProfile> => {
    const dto = unwrap(
      await clientFetch<ApiEnvelope<NursePublicProfileDto>>(`${NURSES_BASE}/${nurseId}/profile`),
    );

    const services: NurseProfileServiceRow[] = dto.services.map((s) => ({
      variantId: s.variantId,
      displayName: s.displayName,
      priceIrr: s.priceIrr,
      priceUnit: s.priceUnit,
      sessionCount: s.sessionCount,
    }));

    const latestReview: NurseReviewSnippet | null = dto.latestReview
      ? {
          rating: dto.latestReview.rating,
          body: dto.latestReview.body ?? '',
          authorMasked: dto.latestReview.authorMasked ?? '',
          createdAt: dto.latestReview.createdAt,
        }
      : null;

    return {
      nurseId: dto.nurseId,
      nurseName: dto.nurseName,
      avatarUrl: dto.avatarUrl,
      bio: dto.bio || null,
      yearsExperience: dto.yearsExperience,
      averageRating: dto.averageRating,
      totalReviews: dto.totalReviews,
      totalCompletedBookings: dto.totalCompletedBookings,
      isVerified: dto.isVerified,
      inoMembership: dto.inoMembership,
      attributeChips: dto.attributeChips,
      services,
      latestReview,
      // Not carried by the public profile DTO; the same-gender intent for booking comes from the C1
      // filter carried through the query string, not this field. Unused by the C3 page.
      nurseGender: 'female',
    };
  },
};
