import { sleep } from '@/utils';
import { ApiError } from '@/lib/api/errors';
import type { Paginated } from '@/lib/api/types';
import { SEARCH_PAGE_SIZE } from '../constants';
import type {
  NurseProfile,
  NurseProfileServiceRow,
  NurseSearchFilters,
  NurseSearchResult,
  SearchApi,
} from '../types';
import { SEED_NURSES, type SeedNurse, type SeedVariant } from './seed';

const MOCK_LATENCY_MS = 300;

/** Flatten every seeded nurse's variants into candidate search rows (one row per variant×area). */
function allRows(): { nurse: SeedNurse; variant: SeedVariant }[] {
  return SEED_NURSES.flatMap((nurse) => nurse.variants.map((variant) => ({ nurse, variant })));
}

/**
 * The b7 geography rule: a **city-only** search (no `districtId`) matches every row in the city; a
 * **district** search matches that district's rows **plus** whole-city (`null`) rows.
 */
function matchesDistrict(rowDistrictId: number | null, filterDistrictId?: number): boolean {
  if (filterDistrictId == null) return true;
  return rowDistrictId === filterDistrictId || rowDistrictId === null;
}

function withinPrice(priceIrr: string, min?: string, max?: string): boolean {
  const value = BigInt(priceIrr);
  if (min != null && min !== '' && value < BigInt(min)) return false;
  if (max != null && max !== '' && value > BigInt(max)) return false;
  return true;
}

function toResult(nurse: SeedNurse, variant: SeedVariant): NurseSearchResult {
  return {
    nurseId: nurse.nurseId,
    variantId: variant.variantId,
    serviceCategoryId: variant.serviceCategoryId,
    nurseName: nurse.nurseName,
    avatarUrl: nurse.avatarUrl,
    isVerified: true,
    averageRating: nurse.averageRating,
    totalReviews: nurse.totalReviews,
    totalCompletedBookings: nurse.totalCompletedBookings,
    distanceKm: variant.distanceKm,
    priceFromIrr: variant.priceIrr,
    priceUnit: variant.priceUnit,
    nurseGender: nurse.gender,
    cityId: variant.cityId,
    districtId: variant.districtId,
  };
}

/**
 * In-memory mock behind the `SearchApi` seam. Reproduces the b7 filter + geography + rating-sort
 * semantics over verified-only fixtures, so C1/C2/C3 (incl. the empty state and the caching revert)
 * demo end-to-end. Mirrors the real shapes for a one-line swap once the backend join/profile endpoints
 * land (`USE_SEARCH_MOCK = false`).
 */
export const searchMockApi: SearchApi = {
  searchNurses: async (filters: NurseSearchFilters): Promise<Paginated<NurseSearchResult>> => {
    await sleep(MOCK_LATENCY_MS);

    if (!(filters.serviceCategoryId > 0) || !(filters.cityId > 0)) {
      throw new ApiError(400, 'service_category_id and city_id are required', 'invalid_filters');
    }
    if (filters.priceMin && filters.priceMax && BigInt(filters.priceMin) > BigInt(filters.priceMax)) {
      throw new ApiError(400, 'min_price must not exceed max_price', 'invalid_price_range');
    }

    const matched = allRows()
      .filter(({ nurse, variant }) => {
        if (variant.serviceCategoryId !== filters.serviceCategoryId) return false;
        if (variant.cityId !== filters.cityId) return false;
        if (!matchesDistrict(variant.districtId, filters.districtId)) return false;
        if (filters.nurseGender && nurse.gender !== filters.nurseGender) return false;
        if (filters.priceUnit && variant.priceUnit !== filters.priceUnit) return false;
        if (!withinPrice(variant.priceIrr, filters.priceMin, filters.priceMax)) return false;
        return true;
      })
      // Rating desc, tiebroken by review count then ids so paging is deterministic (contract order).
      .sort(
        (a, b) =>
          b.nurse.averageRating - a.nurse.averageRating ||
          b.nurse.totalReviews - a.nurse.totalReviews ||
          a.nurse.nurseId - b.nurse.nurseId ||
          a.variant.variantId - b.variant.variantId,
      )
      .map(({ nurse, variant }) => toResult(nurse, variant));

    const pageSize = filters.pageSize || SEARCH_PAGE_SIZE;
    const page = filters.page || 1;
    const start = (page - 1) * pageSize;
    return { items: matched.slice(start, start + pageSize), total: matched.length, page, pageSize };
  },

  getNurseProfile: async (nurseId: number): Promise<NurseProfile> => {
    await sleep(MOCK_LATENCY_MS);
    const nurse = SEED_NURSES.find((candidate) => candidate.nurseId === nurseId);
    if (!nurse) throw new ApiError(404, 'Nurse not found', 'not_found');

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

    return {
      nurseId: nurse.nurseId,
      nurseName: nurse.nurseName,
      avatarUrl: nurse.avatarUrl,
      bio: nurse.bio,
      yearsExperience: nurse.yearsExperience,
      averageRating: nurse.averageRating,
      totalReviews: nurse.totalReviews,
      totalCompletedBookings: nurse.totalCompletedBookings,
      isVerified: true,
      inoMembership: nurse.inoMembership,
      attributeChips: nurse.attributeChips,
      services,
      latestReview: nurse.latestReview,
      nurseGender: nurse.gender,
    };
  },
};
