"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.representativesService = void 0; const client_1 = require("@prisma/client"); const database_1 = require("../../../config/database"); const logger_1 = require("../../../utils/logger"); const error_handler_1 = require("../../../middleware/error-handler"); const represent_api_client_1 = require("./represent-api.client"); const postal_codes_service_1 = require("../postal-codes/postal-codes.service"); function deduplicateReps(reps) { const seen = new Set(); return reps.filter((rep) => { const key = `${rep.name}|${rep.elected_office}`; if (seen.has(key)) return false; seen.add(key); return true; }); } exports.representativesService = { async lookupByPostalCode(code, forceRefresh = false) { // 1. Check cache unless forcing refresh if (!forceRefresh) { const cached = await database_1.prisma.representative.findMany({ where: { postalCode: code }, }); if (cached.length > 0) { const postalInfo = await postal_codes_service_1.postalCodesService.findByPostalCode(code); return { source: 'cache', postalCode: code, location: { city: postalInfo?.city ?? null, province: postalInfo?.province ?? null, }, representatives: cached, }; } } // 2. Call Represent API const apiResponse = await represent_api_client_1.representApiClient.getByPostalCode(code); // Merge centroid + concordance reps and deduplicate const allReps = [ ...(apiResponse.representatives_centroid || []), ...(apiResponse.representatives_concordance || []), ]; const uniqueReps = deduplicateReps(allReps); // 3. Fire-and-forget cache write const cacheWrite = async () => { try { // Delete old cached reps for this postal code await database_1.prisma.representative.deleteMany({ where: { postalCode: code } }); // Cache new reps if (uniqueReps.length > 0) { await database_1.prisma.representative.createMany({ data: uniqueReps.map((rep) => ({ postalCode: code, name: rep.name || null, email: rep.email || null, districtName: rep.district_name || null, electedOffice: rep.elected_office || null, partyName: rep.party_name || null, representativeSetName: rep.representative_set_name || null, url: rep.url || null, photoUrl: rep.photo_url || null, offices: rep.offices ? rep.offices : client_1.Prisma.JsonNull, })), }); } // Upsert postal code cache const coords = apiResponse.centroid?.coordinates; await postal_codes_service_1.postalCodesService.upsert({ postalCode: code, city: apiResponse.city, province: apiResponse.province, centroidLat: coords ? coords[1] : null, centroidLng: coords ? coords[0] : null, }); } catch (err) { logger_1.logger.error('Failed to cache representatives', { postalCode: code, error: err }); } }; // Don't await — fire and forget cacheWrite(); // 4. Build response from API data return { source: 'api', postalCode: code, location: { city: apiResponse.city ?? null, province: apiResponse.province ?? null, }, representatives: uniqueReps.map((rep) => ({ id: null, postalCode: code, name: rep.name || null, email: rep.email || null, districtName: rep.district_name || null, electedOffice: rep.elected_office || null, partyName: rep.party_name || null, representativeSetName: rep.representative_set_name || null, url: rep.url || null, photoUrl: rep.photo_url || null, offices: rep.offices ?? null, cachedAt: new Date().toISOString(), })), }; }, async findAll(filters) { const { page, limit, search, postalCode } = filters; const skip = (page - 1) * limit; const where = {}; if (postalCode) { where.postalCode = postalCode; } if (search) { where.OR = [ { name: { contains: search, mode: 'insensitive' } }, { email: { contains: search, mode: 'insensitive' } }, { districtName: { contains: search, mode: 'insensitive' } }, { electedOffice: { contains: search, mode: 'insensitive' } }, ]; } const [representatives, total] = await Promise.all([ database_1.prisma.representative.findMany({ where, skip, take: limit, orderBy: { cachedAt: 'desc' }, }), database_1.prisma.representative.count({ where }), ]); return { representatives, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) }, }; }, async findById(id) { const rep = await database_1.prisma.representative.findUnique({ where: { id } }); if (!rep) { throw new error_handler_1.AppError(404, 'Representative not found', 'REPRESENTATIVE_NOT_FOUND'); } return rep; }, async clearByPostalCode(code) { const result = await database_1.prisma.representative.deleteMany({ where: { postalCode: code }, }); return { deleted: result.count, postalCode: code }; }, async deleteById(id) { const existing = await database_1.prisma.representative.findUnique({ where: { id } }); if (!existing) { throw new error_handler_1.AppError(404, 'Representative not found', 'REPRESENTATIVE_NOT_FOUND'); } await database_1.prisma.representative.delete({ where: { id } }); }, async testApiConnection() { return represent_api_client_1.representApiClient.testConnection(); }, async getCacheStats() { const [totalReps, postalCodesWithReps, totalPostalCodes] = await Promise.all([ database_1.prisma.representative.count(), database_1.prisma.representative.groupBy({ by: ['postalCode'] }).then((g) => g.length), database_1.prisma.postalCodeCache.count(), ]); return { totalRepresentatives: totalReps, postalCodesWithRepresentatives: postalCodesWithReps, totalPostalCodes, }; }, }; //# sourceMappingURL=representatives.service.js.map