import { apiClient } from './api-client'; // ─── Types ────────────────────────────────────────────── export interface ValuationRequest { propertyType: string; area: number; district: string; city: string; bedrooms?: number; bathrooms?: number; floors?: number; frontage?: number; roadWidth?: number; yearBuilt?: number; hasLegalPaper?: boolean; latitude?: number; longitude?: number; /** Optional project ID for project-based valuation */ projectId?: string; /** Image file for visual analysis */ imageUrl?: string; /** Description text for AI context */ description?: string; /** Request deep analysis (confidence explanation, more drivers) */ deepAnalysis?: boolean; } export interface ValuationComparable { id: string; title: string; address: string; district: string; priceVND: string; areaM2: number; pricePerM2: number; similarity: number; propertyType?: string; bedrooms?: number; bathrooms?: number; floors?: number; yearBuilt?: number; latitude?: number; longitude?: number; } export interface PriceDriver { feature: string; impact: number; direction: 'positive' | 'negative'; /** Human-readable explanation of this driver's impact */ explanation?: string; } export interface MarketContext { avgPricePerM2: number; medianPrice: number; priceGrowthYoY: number; demandIndex: number; supplyCount: number; avgDaysOnMarket: number; district: string; city: string; period: string; } export interface ValuationHistoryPoint { date: string; estimatedPriceVND: number; confidence: number; } export interface ConfidenceExplanation { level: 'high' | 'medium' | 'low'; score: number; factors: Array<{ factor: string; contribution: 'positive' | 'negative'; detail: string; }>; summary: string; } export interface ValuationResult { id: string; estimatedPriceVND: number; confidence: number; pricePerM2: number; priceRangeLow: number; priceRangeHigh: number; comparables: ValuationComparable[]; priceDrivers: PriceDriver[]; modelVersion: string; createdAt: string; /** Enhanced fields from deep analysis */ confidenceExplanation?: ConfidenceExplanation; marketContext?: MarketContext; valuationHistory?: ValuationHistoryPoint[]; } export interface ValuationHistoryItem { id: string; propertyType: string; district: string; city: string; area: number; estimatedPriceVND: number; confidence: number; createdAt: string; } export interface ValuationHistoryResponse { data: ValuationHistoryItem[]; total: number; page: number; limit: number; } export interface BatchValuationRequest { properties: ValuationRequest[]; } export interface BatchValuationResponse { results: ValuationResult[]; totalProcessed: number; errors: Array<{ index: number; message: string }>; } export interface ValuationCompareRequest { propertyIds: string[]; } export interface ValuationCompareResponse { properties: Array<{ id: string; valuation: ValuationResult; property: { title: string; district: string; city: string; area: number; propertyType: string; }; }>; } export interface ProjectSuggestion { id: string; name: string; district: string; city: string; type: string; } // ─── API ──────────────────────────────────────────────── export const valuationApi = { /** Request AVM estimate via POST /analytics/valuation */ predict: (data: ValuationRequest) => { // Build request body with all fields const body: Record = { propertyType: data.propertyType, areaM2: data.area, district: data.district, city: data.city, }; if (data.bedrooms != null) body['bedrooms'] = data.bedrooms; if (data.bathrooms != null) body['bathrooms'] = data.bathrooms; if (data.floors != null) body['floors'] = data.floors; if (data.frontage != null) body['frontage'] = data.frontage; if (data.roadWidth != null) body['roadWidth'] = data.roadWidth; if (data.yearBuilt != null) body['yearBuilt'] = data.yearBuilt; if (data.hasLegalPaper != null) body['hasLegalPaper'] = data.hasLegalPaper; if (data.latitude) body['latitude'] = data.latitude; if (data.longitude) body['longitude'] = data.longitude; if (data.projectId) body['projectId'] = data.projectId; if (data.imageUrl) body['imageUrl'] = data.imageUrl; if (data.description) body['description'] = data.description; if (data.deepAnalysis) body['deepAnalysis'] = data.deepAnalysis; return apiClient.post('/analytics/valuation', body); }, /** Batch valuation: POST /analytics/valuation/batch (max 50) */ batchPredict: (data: BatchValuationRequest) => apiClient.post('/analytics/valuation/batch', data), /** Get valuation history for a property: GET /analytics/valuation/history/:propertyId */ getPropertyHistory: (propertyId: string) => apiClient.get<{ data: ValuationHistoryPoint[] }>( `/analytics/valuation/history/${propertyId}`, ), /** Compare valuations: POST /analytics/valuation/compare */ compare: (data: ValuationCompareRequest) => apiClient.post('/analytics/valuation/compare', data), /** User valuation history (paginated) */ getHistory: (page = 1, limit = 10) => apiClient.get( `/analytics/valuation/user-history?page=${page}&limit=${limit}`, ), /** Get single valuation by ID */ getById: (id: string) => apiClient.get(`/analytics/valuation/${id}`), /** Predict for existing listing */ predictForListing: (listingId: string) => apiClient.post('/analytics/valuation', { propertyId: listingId, }), /** Search projects for autocomplete */ searchProjects: (query: string) => apiClient.get<{ data: ProjectSuggestion[] }>( `/projects/search?q=${encodeURIComponent(query)}&limit=10`, ), };