import { Injectable, Logger } from "@nestjs/common"; import { Types } from "mongoose"; import { DEFAULT_EXTERNAL_INQUIRY_FLAGS, ExternalInquiryFlags, ExternalInquiryType, mergeExternalInquiryFlags, MockInquiryCompanyContext, } from "src/common/types/external-inquiry.types"; import { SystemSettingsService } from "src/system-settings/system-settings.service"; import { ClientDbService } from "./entities/db-service/client.db.service"; @Injectable() export class ExternalInquirySettingsService { private readonly logger = new Logger(ExternalInquirySettingsService.name); private clientCache = new Map(); private readonly cacheTtlMs = 15_000; constructor( private readonly systemSettingsService: SystemSettingsService, private readonly clientDbService: ClientDbService, ) {} private cacheKey(ref?: string | Types.ObjectId | null): string { if (ref != null && String(ref).trim()) return `id:${String(ref)}`; const code = process.env.CLIENT_ID; return code ? `env:${code}` : "env:default"; } private async loadClient( clientRef?: string | Types.ObjectId | null, ): Promise { const key = this.cacheKey(clientRef); const cached = this.clientCache.get(key); const now = Date.now(); if (cached && now - cached.at < this.cacheTtlMs) { return cached.doc; } let doc: any = null; if (clientRef != null && Types.ObjectId.isValid(String(clientRef))) { doc = await this.clientDbService.findOne({ _id: new Types.ObjectId(String(clientRef)), }); } if (!doc) { const code = Number(process.env.CLIENT_ID); if (Number.isFinite(code)) { doc = await this.clientDbService.findOne({ clientCode: code }); } } this.clientCache.set(key, { doc, at: now }); return doc; } invalidateClientCache(clientId?: string): void { if (clientId) { this.clientCache.delete(`id:${clientId}`); } else { this.clientCache.clear(); } } async getFlagsForClient( clientRef?: string | Types.ObjectId | null, ): Promise { const client = await this.loadClient(clientRef); return mergeExternalInquiryFlags(client?.settings?.externalInquiries); } /** * Live HTTP when global master switch is on AND the per-insurer flag is true. */ async isInquiryLive( type: ExternalInquiryType, clientRef?: string | Types.ObjectId | null, ): Promise { if (!(await this.systemSettingsService.isSandHubLiveEnabled())) { return false; } const flags = await this.getFlagsForClient(clientRef); return flags[type] === true; } /** Company fields injected into mocked plate/car-body inquiry payloads. */ async getMockCompanyContext( clientRef?: string | Types.ObjectId | null, ): Promise { const client = await this.loadClient(clientRef); if (client) { const name = typeof client.clientName === "string" ? client.clientName : client.clientName?.persian || client.clientName?.english || ""; return { companyId: String(client.clientCode ?? process.env.CLIENT_ID ?? "15"), companyName: name || process.env.CLIENT_NAME || "بیمه", }; } return { companyId: String(process.env.CLIENT_ID ?? "15"), companyName: process.env.CLIENT_NAME ?? "بیمه سامان", }; } async getEffectiveLiveFlags( clientRef?: string | Types.ObjectId | null, ): Promise { const global = await this.systemSettingsService.isSandHubLiveEnabled(); const flags = await this.getFlagsForClient(clientRef); const effective = { ...DEFAULT_EXTERNAL_INQUIRY_FLAGS }; for (const key of Object.keys(effective) as ExternalInquiryType[]) { effective[key] = global && flags[key] === true; } return effective; } }