import { Injectable, Logger, NotFoundException } from "@nestjs/common"; import { resolveFanavaranClientKey } from "src/core/config/fanavaran-client.config"; import { FANAVARAN_REMOTE_LOOKUPS, type FanavaranRemoteLookupDefinition, TEJARAT_STATIC_ACCIDENT_FILES, } from "src/fanavaran/fanavaran-lookup.config"; import { FanavaranLookupService } from "src/fanavaran/fanavaran-lookup.service"; import { LookupDbService } from "./entities/db-service/lookup.db.service"; type TejaratAccidentReasonRow = { id: number; persianLabel: string; fanavaranID: number; }; type TejaratAccidentLabelRow = { id: number; persianLabel: string; }; @Injectable() export class LookupsService { private readonly logger = new Logger(LookupsService.name); constructor( private readonly lookupDbService: LookupDbService, private readonly fanavaranLookupService: FanavaranLookupService, ) {} private activeClientKey() { return resolveFanavaranClientKey(); } listFanavaranRemoteLookups(): FanavaranRemoteLookupDefinition[] { return FANAVARAN_REMOTE_LOOKUPS; } private findRemoteLookup(name: string) { const lookup = FANAVARAN_REMOTE_LOOKUPS.find((item) => item.name === name); if (!lookup) { throw new NotFoundException(`Unknown Fanavaran remote lookup: ${name}`); } return lookup; } /** * Returns stored `response` for a lookup document by `name` in the lookups collection. * Legacy Tejarat seed data — used as fallback when live fetch is unavailable. */ async getLookup(lookupName: string): Promise { const doc = await this.lookupDbService.findOne({ name: lookupName }); if (!doc) { this.logger.warn(`Lookup not found in database: ${lookupName}`); throw new NotFoundException(`Lookup "${lookupName}" not found`); } return doc.response; } async getClientRemoteLookup(lookupName: string): Promise { const clientKey = this.activeClientKey(); const definition = this.findRemoteLookup(lookupName); return this.fanavaranLookupService.getRemoteLookup( clientKey, definition.url, definition.cacheFile, clientKey === "tejaratno" ? async () => this.getLookup(lookupName) : undefined, ); } async getAccidentCauses(): Promise { return await this.getClientRemoteLookup("accident-causes"); } async getAccidentReportType(): Promise { return await this.getClientRemoteLookup("accident-report-type"); } async getVehicleUseTypes(): Promise { return await this.getClientRemoteLookup("vehicle-use-types"); } async getDmgPayMethod(): Promise { return await this.getClientRemoteLookup("dmg-pay-method"); } async getDrivingLicenceTypes(): Promise { return await this.getClientRemoteLookup("driving-licence-types"); } async getAccidentCulpritType(): Promise { return await this.getClientRemoteLookup("accident-culprit-type"); } async getInspectionPlace(): Promise { return await this.getClientRemoteLookup("inspection-place"); } async getDropAmountStatus(): Promise { return await this.getClientRemoteLookup("drop-amount-status"); } async getCarComponents(): Promise { return await this.getClientRemoteLookup("car-components"); } async getAccidentLevel(): Promise { return await this.getClientRemoteLookup("accident-level"); } async getExpertStatus(): Promise { return await this.getClientRemoteLookup("expert-status"); } async getVehicleKinds(): Promise { return await this.getClientRemoteLookup("vehicle-kinds"); } async inquiryByVin(vin: string): Promise { const clientKey = this.activeClientKey(); return this.fanavaranLookupService.inquiryByVin(clientKey, vin); } async getAccidentWay(): Promise<{ id: number; label: string }[]> { const clientKey = this.activeClientKey(); const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentWay; const cached = await this.fanavaranLookupService.readCacheFile< TejaratAccidentLabelRow[] >(clientKey, fileName); const raw = cached ?? (clientKey === "tejaratno" ? await this.fanavaranLookupService.readTejaratStaticAccidentFile< TejaratAccidentLabelRow[] >(fileName) : null); if (!raw) { this.logger.warn( `No accident-way lookup for client ${clientKey}; falling back to tejarat static file`, ); const fallback = await this.fanavaranLookupService.readTejaratStaticAccidentFile< TejaratAccidentLabelRow[] >(fileName); return fallback.map((item) => ({ id: item.id, label: item.persianLabel, })); } return raw.map((item) => ({ id: item.id, label: item.persianLabel })); } async getAccidentReason(): Promise< { id: number; label: string; fanavaran: number }[] > { const clientKey = this.activeClientKey(); if (clientKey === "parsian") { const cacheFile = "accident-reason-options.json"; const cached = await this.fanavaranLookupService.readCacheFile< { id: number; label: string; fanavaran: number }[] >(clientKey, cacheFile); if (cached) { return cached; } const causes = await this.getAccidentCauses(); const mapped = this.fanavaranLookupService.mapFanavaranAccidentCausesToReasonOptions( causes, ); await this.fanavaranLookupService.writeCacheFile( clientKey, cacheFile, mapped, ); return mapped; } const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentReason; const cached = await this.fanavaranLookupService.readCacheFile< TejaratAccidentReasonRow[] >(clientKey, fileName); const raw = cached ?? (await this.fanavaranLookupService.readTejaratStaticAccidentFile< TejaratAccidentReasonRow[] >(fileName)); return raw.map((item) => ({ id: item.id, label: item.persianLabel, fanavaran: item.fanavaranID, })); } async getAccidentType(): Promise<{ id: number; label: string }[]> { const clientKey = this.activeClientKey(); const fileName = TEJARAT_STATIC_ACCIDENT_FILES.accidentType; const cached = await this.fanavaranLookupService.readCacheFile< TejaratAccidentLabelRow[] >(clientKey, fileName); const raw = cached ?? (clientKey === "tejaratno" ? await this.fanavaranLookupService.readTejaratStaticAccidentFile< TejaratAccidentLabelRow[] >(fileName) : null); if (!raw) { this.logger.warn( `No accident-type lookup for client ${clientKey}; falling back to tejarat static file`, ); const fallback = await this.fanavaranLookupService.readTejaratStaticAccidentFile< TejaratAccidentLabelRow[] >(fileName); return fallback.map((item) => ({ id: item.id, label: item.persianLabel, })); } return raw.map((item) => ({ id: item.id, label: item.persianLabel })); } async getAccidentFields(): Promise<{ accidentWay: { id: number; label: string }[]; accidentReason: { id: number; label: string; fanavaran: number }[]; accidentType: { id: number; label: string }[]; client: string; }> { const [accidentWay, accidentReason, accidentType] = await Promise.all([ this.getAccidentWay(), this.getAccidentReason(), this.getAccidentType(), ]); return { client: this.activeClientKey(), accidentWay, accidentReason, accidentType, }; } }