"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CarListingService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const car_listing_entity_1 = require("../../common/carmarketplace/car-listing.entity");
const car_catalog_entry_entity_1 = require("../../common/carmarketplace/car-catalog-entry.entity");
const marketplace_listing_status_enum_1 = require("../../common/marketplace/marketplace-listing-status.enum");
let CarListingService = class CarListingService {
    constructor(carListingRepository, carCatalogRepository) {
        this.carListingRepository = carListingRepository;
        this.carCatalogRepository = carCatalogRepository;
    }
    async getConfig(page, limit) {
        const catalog = await this.carCatalogRepository.find({
            select: {
                make: true,
                makeArabic: true,
                model: true,
                modelArabic: true,
                year: true,
            },
            order: {
                make: "ASC",
                model: "ASC",
                year: "ASC",
            },
        });
        const makesMap = new Map();
        const yearsSet = new Set();
        for (const entry of catalog) {
            yearsSet.add(entry.year);
            let makeBucket = makesMap.get(entry.make);
            if (!makeBucket) {
                makeBucket = {
                    make: entry.make,
                    makeArabic: entry.makeArabic || null,
                    modelsMap: new Map(),
                };
                makesMap.set(entry.make, makeBucket);
            }
            else if (!makeBucket.makeArabic && entry.makeArabic) {
                makeBucket.makeArabic = entry.makeArabic;
            }
            let modelBucket = makeBucket.modelsMap.get(entry.model);
            if (!modelBucket) {
                modelBucket = {
                    model: entry.model,
                    modelArabic: entry.modelArabic || null,
                    years: new Set(),
                };
                makeBucket.modelsMap.set(entry.model, modelBucket);
            }
            else if (!modelBucket.modelArabic && entry.modelArabic) {
                modelBucket.modelArabic = entry.modelArabic;
            }
            modelBucket.years.add(entry.year);
        }
        const makesArray = Array.from(makesMap.values()).map((makeBucket) => ({
            make: makeBucket.make,
            makeArabic: makeBucket.makeArabic,
            models: Array.from(makeBucket.modelsMap.values()).map((modelBucket) => ({
                model: modelBucket.model,
                modelArabic: modelBucket.modelArabic,
                years: Array.from(modelBucket.years).sort((left, right) => left - right),
            })),
        }));
        const totalMakes = makesArray.length;
        let paginatedMakes = makesArray;
        if (page && limit) {
            const startIndex = (page - 1) * limit;
            const endIndex = startIndex + limit;
            paginatedMakes = makesArray.slice(startIndex, endIndex);
        }
        return {
            years: Array.from(yearsSet).sort((left, right) => left - right),
            makes: paginatedMakes,
            totalEntries: catalog.length,
            totalMakes,
            page: page || 1,
            limit: limit || totalMakes,
            filters: {
                condition: ["Excellent", "Very Good", "Good", "Fair", "Needs Repair"],
                fuelType: ["Petrol", "Diesel", "Hybrid", "Electric"],
                transmissionType: ["Automatic", "Manual"],
            },
        };
    }
    async getCatalogEntryDefaults(make, model, year) {
        const entry = await this.carCatalogRepository.findOne({
            where: {
                make: make.trim(),
                model: model.trim(),
                year,
            },
            select: {
                make: true,
                model: true,
                year: true,
                countryOfOrigin: true,
                bodyStyles: true,
                gearbox: true,
                engine: true,
            },
        });
        if (!entry) {
            return null;
        }
        const gearbox = String(entry.gearbox || "").toLowerCase();
        const engine = String(entry.engine || "").toLowerCase();
        const transmissionType = gearbox.includes("auto")
            ? "Automatic"
            : gearbox.includes("manual")
                ? "Manual"
                : null;
        const fuelType = engine.includes("diesel")
            ? "Diesel"
            : engine.includes("hybrid")
                ? "Hybrid"
                : engine.includes("electric")
                    ? "Electric"
                    : engine.includes("petrol") || engine.includes("gasoline")
                        ? "Petrol"
                        : null;
        return {
            entry,
            defaults: {
                origin: entry.countryOfOrigin || null,
                bodyType: entry.bodyStyles || null,
                transmissionType,
                fuelType,
            },
        };
    }
    decorateListing(listing) {
        const baseFeatures = listing.features && typeof listing.features === "object" ? listing.features : null;
        const structuredFeatures = this.buildStructuredFeatures(listing);
        const mergedFeatures = baseFeatures
            ? { ...structuredFeatures, ...baseFeatures }
            : structuredFeatures;
        return {
            ...listing,
            features: mergedFeatures,
            isExpired: this.isExpired(listing.expiresAt),
            contact: {
                whatsappUrl: this.buildWhatsappUrl(listing.sellerMobile),
            },
        };
    }
    async createListing(userId, dto) {
        await this.expireListings();
        await this.ensureCatalogEntryExists(dto.make, dto.model, dto.year);
        const normalizedStructured = this.normalizeStructuredFeatures(dto.features);
        const listing = this.carListingRepository.create({
            sellerUserId: userId,
            make: dto.make.trim(),
            model: dto.model.trim(),
            year: dto.year,
            trim: this.nullable(dto.trim),
            exteriorColor: this.nullable(dto.exteriorColor),
            interiorColor: this.nullable(dto.interiorColor),
            fuelType: this.nullable(dto.fuelType),
            transmissionType: this.nullable(dto.transmissionType),
            mileage: this.numberOrNull(dto.mileage),
            carCondition: this.nullable(dto.carCondition),
            warrantyStatus: this.nullable(dto.warrantyStatus),
            origin: this.nullable(dto.origin),
            bodyType: this.nullable(dto.bodyType),
            regionalSpecs: this.nullable(dto.regionalSpecs),
            isInsured: typeof dto.isInsured === "boolean" ? Boolean(dto.isInsured) : null,
            title: this.nullable(dto.title),
            features: dto.features || null,
            ...normalizedStructured,
            previousOwners: this.numberOrNull(dto.previousOwners),
            registrationEmirate: this.nullable(dto.registrationEmirate),
            locationName: this.nullable(dto.locationName),
            latitude: this.numberOrNull(dto.latitude),
            longitude: this.numberOrNull(dto.longitude),
            chassisNumber: this.nullable(dto.chassisNumber),
            price: Number(dto.price),
            isNegotiable: Boolean(dto.isNegotiable),
            description: this.nullable(dto.description),
            accidentHistory: this.nullable(dto.accidentHistory),
            repaintingDetails: this.nullable(dto.repaintingDetails),
            maintenanceDetails: this.nullable(dto.maintenanceDetails),
            modificationDetails: this.nullable(dto.modificationDetails),
            additionalNotes: this.nullable(dto.additionalNotes),
            sellerName: this.nullable(dto.sellerName),
            sellerMobile: this.nullable(dto.sellerMobile),
            sellerEmail: this.nullable(dto.sellerEmail),
            photoUrls: this.normalizePhotoUrls(dto.photoUrls),
            isVisible: false,
            validityDays: 30,
            status: marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL,
        });
        return this.carListingRepository.save(listing);
    }
    async findMarketplaceListings(query) {
        await this.expireListings();
        const qb = this.carListingRepository.createQueryBuilder("listing");
        qb.where("listing.status IN (:...statuses)", { statuses: marketplace_listing_status_enum_1.PUBLIC_MARKETPLACE_STATUSES });
        qb.andWhere("(listing.isVisible = :visible OR listing.status = :sold)", { visible: true, sold: marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD });
        qb.orderBy("listing.createdAt", "DESC");
        this.applyQueryFilters(qb, query);
        return qb.getMany();
    }
    async findMyListings(userId) {
        await this.expireListings();
        const listings = await this.carListingRepository.find({
            where: { sellerUserId: userId, parentListingId: null },
            order: { createdAt: "DESC" },
        });
        if (!listings.length)
            return listings;
        const parentIds = listings.map((item) => item.id);
        const pendingEdits = await this.carListingRepository.find({
            where: {
                parentListingId: (0, typeorm_2.In)(parentIds),
                status: marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL,
            },
            order: { createdAt: "DESC" },
        });
        const pendingByParent = new Map();
        for (const edit of pendingEdits) {
            if (!edit.parentListingId)
                continue;
            if (!pendingByParent.has(edit.parentListingId)) {
                pendingByParent.set(edit.parentListingId, edit);
            }
        }
        return listings.map((listing) => {
            const pending = pendingByParent.get(listing.id);
            if (!pending)
                return listing;
            return Object.assign(listing, {
                moderationPending: true,
                pendingEditId: pending.id,
            });
        });
    }
    async findListing(id, requesterUserId, allowPrivate = false) {
        await this.expireListings();
        const listing = await this.carListingRepository.findOne({ where: { id } });
        if (!listing || listing.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE) {
            throw new common_1.NotFoundException(`Car listing with ID ${id} not found.`);
        }
        const isOwner = requesterUserId && Number(listing.sellerUserId) === Number(requesterUserId);
        const isPublic = marketplace_listing_status_enum_1.PUBLIC_MARKETPLACE_STATUSES.includes(listing.status) &&
            (listing.isVisible || listing.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD) &&
            !this.isExpired(listing.expiresAt);
        if (!isPublic && !isOwner && !allowPrivate) {
            throw new common_1.NotFoundException(`Car listing with ID ${id} not found.`);
        }
        if (isOwner && listing.approvedAt) {
            const pendingEdit = await this.carListingRepository.findOne({
                where: {
                    parentListingId: listing.id,
                    status: marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL,
                },
                order: { createdAt: "DESC" },
            });
            if (pendingEdit) {
                const preview = this.buildOwnerPreviewListing(listing, pendingEdit);
                return Object.assign(preview, {
                    moderationPending: true,
                    pendingEditId: pendingEdit.id,
                });
            }
        }
        return listing;
    }
    async updateListing(id, userId, dto) {
        const listing = await this.findListing(id, userId, true);
        this.ensureOwnership(listing, userId);
        if (listing.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD ||
            listing.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.EXPIRED ||
            listing.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE) {
            throw new common_1.ConflictException("This car listing can no longer be edited.");
        }
        if (!listing.approvedAt) {
            await this.applyListingUpdateFields(listing, dto);
            const saved = await this.carListingRepository.save(listing);
            return Object.assign(saved, { moderationPending: false });
        }
        const existingEditRequest = await this.carListingRepository.findOne({
            where: {
                parentListingId: listing.id,
                status: marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL,
            },
            order: { createdAt: "DESC" },
        });
        const editRequest = existingEditRequest ||
            this.carListingRepository.create({
                ...listing,
                id: undefined,
                parentListingId: listing.id,
                status: marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL,
                isVisible: false,
                approvedAt: null,
                approvedByUserId: null,
                expiresAt: null,
                rejectionReason: null,
                createdAt: undefined,
                updatedAt: undefined,
            });
        await this.applyListingUpdateFields(editRequest, dto);
        const savedEditRequest = await this.carListingRepository.save(editRequest);
        return Object.assign(listing, {
            moderationPending: true,
            pendingEditId: savedEditRequest?.id,
        });
    }
    async importCatalogEntries(entries) {
        if (!entries.length) {
            return { insertedOrUpdated: 0 };
        }
        await this.carCatalogRepository.upsert(entries, ["sourceUrl"]);
        return { insertedOrUpdated: entries.length };
    }
    async clearCatalog() {
        await this.carCatalogRepository.clear();
    }
    async getCatalogEntryCount() {
        return this.carCatalogRepository.count();
    }
    async backfillCarListingSpecFields(batchSize = 200) {
        let updated = 0;
        let lastId = 0;
        for (;;) {
            const batch = await this.carListingRepository
                .createQueryBuilder("listing")
                .where("listing.id > :lastId", { lastId })
                .andWhere("listing.features IS NOT NULL")
                .orderBy("listing.id", "ASC")
                .take(batchSize)
                .getMany();
            if (!batch.length)
                break;
            lastId = batch[batch.length - 1].id;
            for (const listing of batch) {
                const derived = this.deriveStructuredFeatures(listing.features);
                if (!derived)
                    continue;
                let touched = false;
                const setIfEmpty = (key, value) => {
                    if (value == null)
                        return;
                    if (listing[key] == null || listing[key] === "") {
                        listing[key] = value;
                        touched = true;
                    }
                };
                setIfEmpty("doors", derived.doors);
                setIfEmpty("cylinders", derived.cylinders);
                setIfEmpty("seating", derived.seating);
                setIfEmpty("horsepower", derived.horsepower);
                setIfEmpty("steeringSide", derived.steeringSide);
                setIfEmpty("engineCapacity", derived.engineCapacity);
                setIfEmpty("safetyFeatures", derived.safetyFeatures);
                setIfEmpty("entertainmentFeatures", derived.entertainmentFeatures);
                setIfEmpty("comfortFeatures", derived.comfortFeatures);
                setIfEmpty("exteriorFeatures", derived.exteriorFeatures);
                if (touched) {
                    await this.carListingRepository.save(listing);
                    updated += 1;
                }
            }
        }
        return { updated };
    }
    async updateListingStatus(id, userId, dto) {
        const listing = await this.findListing(id, userId, true);
        this.ensureOwnership(listing, userId);
        if (!marketplace_listing_status_enum_1.SELLER_MUTABLE_MARKETPLACE_STATUSES.includes(listing.status)) {
            throw new common_1.ConflictException("This car listing status can only be changed by admin.");
        }
        if (![
            marketplace_listing_status_enum_1.MarketplaceListingStatus.ACTIVE,
            marketplace_listing_status_enum_1.MarketplaceListingStatus.PAUSED,
            marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD,
        ].includes(dto.status)) {
            throw new common_1.BadRequestException("Users can only switch listings to active, paused, or sold.");
        }
        listing.status = dto.status;
        if (dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.ACTIVE && !listing.approvedAt) {
            listing.status = marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL;
        }
        if (dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD) {
            listing.isVisible = false;
        }
        return this.carListingRepository.save(listing);
    }
    async removeListing(id, userId) {
        const listing = await this.findListing(id, userId, true);
        this.ensureOwnership(listing, userId);
        listing.status = marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE;
        listing.isVisible = false;
        return this.carListingRepository.save(listing);
    }
    async adminFindListings(query) {
        await this.expireListings();
        const qb = this.carListingRepository.createQueryBuilder("listing");
        qb.orderBy("listing.createdAt", "DESC");
        this.applyQueryFilters(qb, query);
        const sellerUserId = Number(query.sellerUserId);
        if (Number.isFinite(sellerUserId)) {
            qb.andWhere("listing.sellerUserId = :sellerUserId", { sellerUserId });
        }
        return qb.getMany();
    }
    async adminModerateListing(id, adminUserId, dto) {
        const listing = await this.findListing(id, adminUserId, true);
        if (listing.parentListingId) {
            const parentListing = await this.findListing(Number(listing.parentListingId), adminUserId, true);
            if (dto.status && dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.REJECTED) {
                if (dto.rejectionReason !== undefined) {
                    listing.rejectionReason = this.nullable(dto.rejectionReason);
                }
                listing.status = marketplace_listing_status_enum_1.MarketplaceListingStatus.REJECTED;
                listing.isVisible = false;
                await this.carListingRepository.save(listing);
                return parentListing;
            }
            if (dto.validityDays) {
                parentListing.validityDays = dto.validityDays;
                parentListing.expiresAt = this.calculateExpiryDate(dto.validityDays);
            }
            if (typeof dto.isVisible === "boolean") {
                parentListing.isVisible = dto.isVisible;
            }
            const shouldApproveEditRequest = Boolean(dto.status) && dto.status !== marketplace_listing_status_enum_1.MarketplaceListingStatus.PENDING_APPROVAL;
            if (shouldApproveEditRequest) {
                this.applyEditRequestFields(parentListing, listing);
                parentListing.rejectionReason = null;
                if (dto.status) {
                    parentListing.status = dto.status;
                }
                if (dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.ACTIVE ||
                    dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.AVAILABLE) {
                    parentListing.approvedAt = new Date();
                    parentListing.approvedByUserId = adminUserId || null;
                    parentListing.publishedAt = parentListing.publishedAt || new Date();
                    if (!parentListing.expiresAt) {
                        parentListing.expiresAt = this.calculateExpiryDate(parentListing.validityDays);
                    }
                    if (dto.isVisible === undefined) {
                        parentListing.isVisible = parentListing.isVisible ?? true;
                    }
                }
            }
            if (dto.status &&
                [
                    marketplace_listing_status_enum_1.MarketplaceListingStatus.REJECTED,
                    marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE,
                    marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD,
                    marketplace_listing_status_enum_1.MarketplaceListingStatus.EXPIRED,
                ].includes(dto.status)) {
                parentListing.isVisible = false;
            }
            const savedParent = await this.carListingRepository.save(parentListing);
            if (shouldApproveEditRequest) {
                listing.status = marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE;
                listing.isVisible = false;
                await this.carListingRepository.save(listing);
            }
            return savedParent;
        }
        if (dto.validityDays) {
            listing.validityDays = dto.validityDays;
            listing.expiresAt = this.calculateExpiryDate(dto.validityDays);
        }
        if (typeof dto.isVisible === "boolean") {
            listing.isVisible = dto.isVisible;
        }
        if (dto.rejectionReason !== undefined) {
            listing.rejectionReason = this.nullable(dto.rejectionReason);
        }
        if (dto.status) {
            listing.status = dto.status;
            if (dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.ACTIVE ||
                dto.status === marketplace_listing_status_enum_1.MarketplaceListingStatus.AVAILABLE) {
                listing.approvedAt = new Date();
                listing.approvedByUserId = adminUserId || null;
                listing.publishedAt = listing.publishedAt || new Date();
                listing.isVisible = dto.isVisible ?? true;
                listing.expiresAt = this.calculateExpiryDate(listing.validityDays);
            }
            if ([
                marketplace_listing_status_enum_1.MarketplaceListingStatus.REJECTED,
                marketplace_listing_status_enum_1.MarketplaceListingStatus.INACTIVE,
                marketplace_listing_status_enum_1.MarketplaceListingStatus.SOLD,
                marketplace_listing_status_enum_1.MarketplaceListingStatus.EXPIRED,
            ].includes(dto.status)) {
                listing.isVisible = false;
            }
        }
        return this.carListingRepository.save(listing);
    }
    async applyListingUpdateFields(listing, dto) {
        const nextMake = dto.make ? dto.make.trim() : listing.make;
        const nextModel = dto.model ? dto.model.trim() : listing.model;
        const nextYear = dto.year ?? listing.year;
        await this.ensureCatalogEntryExists(nextMake, nextModel, nextYear);
        const nextFeatures = dto.features !== undefined ? dto.features : listing.features;
        const normalizedStructured = dto.features !== undefined
            ? this.normalizeStructuredFeatures(nextFeatures, listing)
            : null;
        Object.assign(listing, {
            make: nextMake,
            model: nextModel,
            year: nextYear,
            trim: this.preserveNullable(dto.trim, listing.trim),
            exteriorColor: this.preserveNullable(dto.exteriorColor, listing.exteriorColor),
            interiorColor: this.preserveNullable(dto.interiorColor, listing.interiorColor),
            fuelType: this.preserveNullable(dto.fuelType, listing.fuelType),
            transmissionType: this.preserveNullable(dto.transmissionType, listing.transmissionType),
            mileage: dto.mileage ?? listing.mileage,
            carCondition: this.preserveNullable(dto.carCondition, listing.carCondition),
            warrantyStatus: this.preserveNullable(dto.warrantyStatus, listing.warrantyStatus),
            origin: this.preserveNullable(dto.origin, listing.origin),
            bodyType: this.preserveNullable(dto.bodyType, listing.bodyType),
            regionalSpecs: this.preserveNullable(dto.regionalSpecs, listing.regionalSpecs),
            isInsured: typeof dto.isInsured === "boolean" ? Boolean(dto.isInsured) : listing.isInsured,
            title: this.preserveNullable(dto.title, listing.title),
            features: nextFeatures,
            previousOwners: dto.previousOwners ?? listing.previousOwners,
            registrationEmirate: this.preserveNullable(dto.registrationEmirate, listing.registrationEmirate),
            locationName: this.preserveNullable(dto.locationName, listing.locationName),
            latitude: dto.latitude ?? listing.latitude,
            longitude: dto.longitude ?? listing.longitude,
            chassisNumber: this.preserveNullable(dto.chassisNumber, listing.chassisNumber),
            price: dto.price ?? listing.price,
            isNegotiable: dto.isNegotiable ?? listing.isNegotiable,
            description: this.preserveNullable(dto.description, listing.description),
            accidentHistory: this.preserveNullable(dto.accidentHistory, listing.accidentHistory),
            repaintingDetails: this.preserveNullable(dto.repaintingDetails, listing.repaintingDetails),
            maintenanceDetails: this.preserveNullable(dto.maintenanceDetails, listing.maintenanceDetails),
            modificationDetails: this.preserveNullable(dto.modificationDetails, listing.modificationDetails),
            additionalNotes: this.preserveNullable(dto.additionalNotes, listing.additionalNotes),
            sellerName: this.preserveNullable(dto.sellerName, listing.sellerName),
            sellerMobile: this.preserveNullable(dto.sellerMobile, listing.sellerMobile),
            sellerEmail: this.preserveNullable(dto.sellerEmail, listing.sellerEmail),
            photoUrls: dto.photoUrls ? this.normalizePhotoUrls(dto.photoUrls) : listing.photoUrls,
        });
        if (normalizedStructured) {
            Object.assign(listing, normalizedStructured);
        }
    }
    applyEditRequestFields(parent, editRequest) {
        Object.assign(parent, {
            make: editRequest.make,
            model: editRequest.model,
            year: editRequest.year,
            trim: editRequest.trim,
            exteriorColor: editRequest.exteriorColor,
            interiorColor: editRequest.interiorColor,
            fuelType: editRequest.fuelType,
            transmissionType: editRequest.transmissionType,
            mileage: editRequest.mileage,
            carCondition: editRequest.carCondition,
            warrantyStatus: editRequest.warrantyStatus,
            origin: editRequest.origin,
            bodyType: editRequest.bodyType,
            regionalSpecs: editRequest.regionalSpecs,
            isInsured: editRequest.isInsured,
            title: editRequest.title,
            features: editRequest.features,
            doors: editRequest.doors,
            cylinders: editRequest.cylinders,
            seating: editRequest.seating,
            horsepower: editRequest.horsepower,
            steeringSide: editRequest.steeringSide,
            engineCapacity: editRequest.engineCapacity,
            safetyFeatures: editRequest.safetyFeatures,
            entertainmentFeatures: editRequest.entertainmentFeatures,
            comfortFeatures: editRequest.comfortFeatures,
            exteriorFeatures: editRequest.exteriorFeatures,
            previousOwners: editRequest.previousOwners,
            registrationEmirate: editRequest.registrationEmirate,
            locationName: editRequest.locationName,
            latitude: editRequest.latitude,
            longitude: editRequest.longitude,
            chassisNumber: editRequest.chassisNumber,
            price: editRequest.price,
            isNegotiable: editRequest.isNegotiable,
            description: editRequest.description,
            accidentHistory: editRequest.accidentHistory,
            repaintingDetails: editRequest.repaintingDetails,
            maintenanceDetails: editRequest.maintenanceDetails,
            modificationDetails: editRequest.modificationDetails,
            additionalNotes: editRequest.additionalNotes,
            sellerName: editRequest.sellerName,
            sellerMobile: editRequest.sellerMobile,
            sellerEmail: editRequest.sellerEmail,
            photoUrls: editRequest.photoUrls,
        });
    }
    buildOwnerPreviewListing(parent, pendingEdit) {
        const preview = { ...parent };
        this.applyEditRequestFields(preview, pendingEdit);
        preview.id = parent.id;
        preview.parentListingId = parent.parentListingId ?? null;
        return preview;
    }
    applyQueryFilters(qb, query) {
        if (query.make) {
            qb.andWhere("LOWER(listing.make) = :make", { make: query.make.trim().toLowerCase() });
        }
        if (query.model) {
            qb.andWhere("LOWER(listing.model) = :model", { model: query.model.trim().toLowerCase() });
        }
        if (query.year) {
            qb.andWhere("listing.year = :year", { year: query.year });
        }
        if (query.carCondition) {
            qb.andWhere("LOWER(listing.carCondition) = :condition", {
                condition: query.carCondition.trim().toLowerCase(),
            });
        }
        if (query.transmissionType) {
            qb.andWhere("LOWER(listing.transmissionType) = :transmissionType", {
                transmissionType: query.transmissionType.trim().toLowerCase(),
            });
        }
        if (query.fuelType) {
            qb.andWhere("LOWER(listing.fuelType) = :fuelType", {
                fuelType: query.fuelType.trim().toLowerCase(),
            });
        }
        if (query.registrationEmirate) {
            qb.andWhere("LOWER(listing.registrationEmirate) = :registrationEmirate", {
                registrationEmirate: query.registrationEmirate.trim().toLowerCase(),
            });
        }
        if (query.status) {
            qb.andWhere("listing.status = :status", { status: query.status });
        }
        const minPrice = Number(query.minPrice);
        if (Number.isFinite(minPrice)) {
            qb.andWhere("listing.price >= :minPrice", { minPrice });
        }
        const maxPrice = Number(query.maxPrice);
        if (Number.isFinite(maxPrice)) {
            qb.andWhere("listing.price <= :maxPrice", { maxPrice });
        }
        const maxMileage = Number(query.maxMileage);
        if (Number.isFinite(maxMileage)) {
            qb.andWhere("listing.mileage <= :maxMileage", { maxMileage });
        }
    }
    async expireListings() {
        await this.carListingRepository
            .createQueryBuilder()
            .update(car_listing_entity_1.CarListing)
            .set({
            status: marketplace_listing_status_enum_1.MarketplaceListingStatus.EXPIRED,
            isVisible: false,
        })
            .where("expiresAt IS NOT NULL")
            .andWhere("expiresAt <= :now", { now: new Date() })
            .andWhere("status IN (:...statuses)", {
            statuses: [
                marketplace_listing_status_enum_1.MarketplaceListingStatus.ACTIVE,
                marketplace_listing_status_enum_1.MarketplaceListingStatus.AVAILABLE,
                marketplace_listing_status_enum_1.MarketplaceListingStatus.PAUSED,
            ],
        })
            .execute();
    }
    calculateExpiryDate(validityDays) {
        const expiresAt = new Date();
        expiresAt.setDate(expiresAt.getDate() + (validityDays || 30));
        return expiresAt;
    }
    ensureOwnership(listing, userId) {
        if (Number(listing.sellerUserId) !== Number(userId)) {
            throw new common_1.ConflictException("Only the listing owner can manage this listing.");
        }
    }
    async ensureCatalogEntryExists(make, model, year) {
        const catalogEntry = await this.carCatalogRepository.findOne({
            where: {
                make: make.trim(),
                model: model.trim(),
                year,
            },
            select: {
                id: true,
            },
        });
        if (!catalogEntry) {
            throw new common_1.BadRequestException("The selected make, model, and year are not available in the imported car catalog.");
        }
    }
    isExpired(expiresAt) {
        return Boolean(expiresAt && expiresAt <= new Date());
    }
    normalizePhotoUrls(photoUrls) {
        const urls = (photoUrls || [])
            .map((item) => String(item || "").trim())
            .filter((item) => item.length > 0);
        if (urls.length > 5) {
            throw new Error("A car listing may not have more than 5 photos.");
        }
        return urls;
    }
    buildWhatsappUrl(mobile) {
        const digits = String(mobile || "").replace(/[^\d]/g, "");
        if (!digits) {
            return null;
        }
        const normalizedMobile = digits.startsWith("971") ? digits : `971${digits.replace(/^0+/, "")}`;
        return `https://wa.me/${normalizedMobile}`;
    }
    nullable(value) {
        const normalized = String(value || "").trim();
        return normalized || null;
    }
    preserveNullable(nextValue, currentValue) {
        if (nextValue === undefined) {
            return currentValue;
        }
        return this.nullable(nextValue);
    }
    numberOrNull(value) {
        return Number.isFinite(Number(value)) ? Number(value) : null;
    }
    normalizeStringArray(value) {
        if (!Array.isArray(value))
            return null;
        const cleaned = value
            .map((item) => String(item || "").trim())
            .filter((item) => item.length > 0);
        return cleaned.length ? cleaned : null;
    }
    normalizeStructuredFeatures(features, current) {
        const safe = features && typeof features === "object" ? features : {};
        const doors = this.nullable(safe.doors);
        const cylinders = this.nullable(safe.cylinders);
        const seating = this.nullable(safe.seating);
        const horsepower = this.nullable(safe.horsepower);
        const steeringSide = this.nullable(safe.steeringSide);
        const engineCapacity = this.nullable(safe.engineCapacity);
        const preserve = (next, prev) => (next === null ? prev : next);
        return {
            doors: current ? preserve(doors, current.doors) : doors,
            cylinders: current ? preserve(cylinders, current.cylinders) : cylinders,
            seating: current ? preserve(seating, current.seating) : seating,
            horsepower: current ? preserve(horsepower, current.horsepower) : horsepower,
            steeringSide: current ? preserve(steeringSide, current.steeringSide) : steeringSide,
            engineCapacity: current ? preserve(engineCapacity, current.engineCapacity) : engineCapacity,
            safetyFeatures: safe.safety !== undefined
                ? (Array.isArray(safe.safety) && safe.safety.length === 0 && current
                    ? current.safetyFeatures ?? null
                    : this.normalizeStringArray(safe.safety))
                : current?.safetyFeatures ?? null,
            entertainmentFeatures: safe.entertainment !== undefined
                ? (Array.isArray(safe.entertainment) && safe.entertainment.length === 0 && current
                    ? current.entertainmentFeatures ?? null
                    : this.normalizeStringArray(safe.entertainment))
                : current?.entertainmentFeatures ?? null,
            comfortFeatures: safe.comfort !== undefined
                ? (Array.isArray(safe.comfort) && safe.comfort.length === 0 && current
                    ? current.comfortFeatures ?? null
                    : this.normalizeStringArray(safe.comfort))
                : current?.comfortFeatures ?? null,
            exteriorFeatures: safe.exterior !== undefined
                ? (Array.isArray(safe.exterior) && safe.exterior.length === 0 && current
                    ? current.exteriorFeatures ?? null
                    : this.normalizeStringArray(safe.exterior))
                : current?.exteriorFeatures ?? null,
        };
    }
    deriveStructuredFeatures(features) {
        const safe = features && typeof features === "object" ? features : null;
        if (!safe)
            return null;
        return {
            doors: this.nullable(safe.doors),
            cylinders: this.nullable(safe.cylinders),
            seating: this.nullable(safe.seating),
            horsepower: this.nullable(safe.horsepower),
            steeringSide: this.nullable(safe.steeringSide),
            engineCapacity: this.nullable(safe.engineCapacity),
            safetyFeatures: safe.safety !== undefined ? this.normalizeStringArray(safe.safety) : null,
            entertainmentFeatures: safe.entertainment !== undefined ? this.normalizeStringArray(safe.entertainment) : null,
            comfortFeatures: safe.comfort !== undefined ? this.normalizeStringArray(safe.comfort) : null,
            exteriorFeatures: safe.exterior !== undefined ? this.normalizeStringArray(safe.exterior) : null,
        };
    }
    buildStructuredFeatures(listing) {
        const hasAny = listing.doors ||
            listing.cylinders ||
            listing.seating ||
            listing.horsepower ||
            listing.steeringSide ||
            listing.engineCapacity ||
            (Array.isArray(listing.safetyFeatures) && listing.safetyFeatures.length) ||
            (Array.isArray(listing.entertainmentFeatures) && listing.entertainmentFeatures.length) ||
            (Array.isArray(listing.comfortFeatures) && listing.comfortFeatures.length) ||
            (Array.isArray(listing.exteriorFeatures) && listing.exteriorFeatures.length);
        if (!hasAny)
            return null;
        return {
            doors: listing.doors || undefined,
            cylinders: listing.cylinders || undefined,
            seating: listing.seating || undefined,
            horsepower: listing.horsepower || undefined,
            steeringSide: listing.steeringSide || undefined,
            engineCapacity: listing.engineCapacity || undefined,
            safety: Array.isArray(listing.safetyFeatures) ? listing.safetyFeatures : undefined,
            entertainment: Array.isArray(listing.entertainmentFeatures) ? listing.entertainmentFeatures : undefined,
            comfort: Array.isArray(listing.comfortFeatures) ? listing.comfortFeatures : undefined,
            exterior: Array.isArray(listing.exteriorFeatures) ? listing.exteriorFeatures : undefined,
        };
    }
};
exports.CarListingService = CarListingService;
exports.CarListingService = CarListingService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(car_listing_entity_1.CarListing)),
    __param(1, (0, typeorm_1.InjectRepository)(car_catalog_entry_entity_1.CarCatalogEntry)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository])
], CarListingService);
//# sourceMappingURL=car-listing.service.js.map