"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); }
};
var CustomersService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CustomersService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const customer_entity_1 = require("../database/entities/customer.entity");
const customer_device_entity_1 = require("../database/entities/customer-device.entity");
const wifi_session_entity_1 = require("../database/entities/wifi-session.entity");
const payment_entity_1 = require("../database/entities/payment.entity");
const voucher_entity_1 = require("../database/entities/voucher.entity");
const audit_log_entity_1 = require("../database/entities/audit-log.entity");
let CustomersService = CustomersService_1 = class CustomersService {
    constructor(customerRepo, customerDeviceRepo, wifiSessionRepo, paymentRepo, voucherRepo, auditLogRepo) {
        this.customerRepo = customerRepo;
        this.customerDeviceRepo = customerDeviceRepo;
        this.wifiSessionRepo = wifiSessionRepo;
        this.paymentRepo = paymentRepo;
        this.voucherRepo = voucherRepo;
        this.auditLogRepo = auditLogRepo;
        this.logger = new common_1.Logger(CustomersService_1.name);
    }
    async findOrCreate(phoneNumber, businessId, deviceInfo) {
        let customer = await this.customerRepo.findOne({
            where: { phoneNumber, businessId },
        });
        if (!customer) {
            customer = this.customerRepo.create({
                phoneNumber,
                businessId,
            });
            customer = await this.customerRepo.save(customer);
            await this.auditLogRepo.save(this.auditLogRepo.create({
                businessId,
                action: 'CUSTOMER_CREATED',
                entityType: 'Customer',
                entityId: customer.id,
                newValue: { phoneNumber },
            }));
            this.logger.log(`Created new customer: ${customer.id} (${phoneNumber})`);
        }
        if (deviceInfo?.macAddress) {
            let device = await this.customerDeviceRepo.findOne({
                where: { customerId: customer.id, macAddress: deviceInfo.macAddress },
            });
            if (!device) {
                device = this.customerDeviceRepo.create({
                    customerId: customer.id,
                    macAddress: deviceInfo.macAddress,
                    ipAddress: deviceInfo.ipAddress,
                    deviceName: deviceInfo.deviceName,
                    deviceType: deviceInfo.deviceType,
                    browserInfo: deviceInfo.browserInfo,
                });
                await this.customerDeviceRepo.save(device);
            }
            else {
                device.lastSeenAt = new Date();
                if (deviceInfo.ipAddress)
                    device.ipAddress = deviceInfo.ipAddress;
                await this.customerDeviceRepo.save(device);
            }
        }
        return customer;
    }
    async findById(id, businessId) {
        const customer = await this.customerRepo.findOne({
            where: { id, businessId },
        });
        if (!customer) {
            throw new common_1.NotFoundException(`Customer ${id} not found`);
        }
        return customer;
    }
    async findByPhone(phoneNumber, businessId) {
        const customer = await this.customerRepo.findOne({
            where: { phoneNumber, businessId },
        });
        if (!customer) {
            throw new common_1.NotFoundException(`Customer with phone ${phoneNumber} not found`);
        }
        return customer;
    }
    async findAll(businessId, filters) {
        const page = filters.page ?? 1;
        const limit = filters.limit ?? 20;
        const sortBy = filters.sortBy ?? 'createdAt';
        const sortOrder = filters.sortOrder ?? 'DESC';
        const where = { businessId };
        if (filters.isBlocked !== undefined) {
            where.isBlocked = filters.isBlocked;
        }
        if (filters.phoneNumber) {
            where.phoneNumber = filters.phoneNumber;
        }
        const conditions = [where];
        if (filters.search) {
            const search = `%${filters.search}%`;
            conditions[0] = {
                ...where,
                phoneNumber: (0, typeorm_2.ILike)(search),
            };
        }
        if (filters.startDate && filters.endDate) {
            conditions[0].createdAt = (0, typeorm_2.Between)(filters.startDate, filters.endDate);
        }
        else if (filters.startDate) {
            conditions[0].createdAt = (0, typeorm_2.MoreThanOrEqual)(filters.startDate);
        }
        const [data, total] = await this.customerRepo.findAndCount({
            where: conditions,
            order: { [sortBy]: sortOrder },
            skip: (page - 1) * limit,
            take: limit,
        });
        const totalPages = Math.ceil(total / limit);
        return { data, total, page, limit, totalPages };
    }
    async blockCustomer(id, businessId, reason) {
        const customer = await this.findById(id, businessId);
        if (customer.isBlocked) {
            throw new common_1.BadRequestException('Customer is already blocked');
        }
        customer.isBlocked = true;
        customer.blockedReason = reason || 'No reason provided';
        const updated = await this.customerRepo.save(customer);
        await this.auditLogRepo.save(this.auditLogRepo.create({
            businessId,
            action: 'CUSTOMER_BLOCKED',
            entityType: 'Customer',
            entityId: customer.id,
            newValue: { isBlocked: true, blockedReason: customer.blockedReason },
        }));
        this.logger.log(`Customer ${id} blocked. Reason: ${customer.blockedReason}`);
        return updated;
    }
    async unblockCustomer(id, businessId) {
        const customer = await this.findById(id, businessId);
        if (!customer.isBlocked) {
            throw new common_1.BadRequestException('Customer is not blocked');
        }
        customer.isBlocked = false;
        customer.blockedReason = null;
        const updated = await this.customerRepo.save(customer);
        await this.auditLogRepo.save(this.auditLogRepo.create({
            businessId,
            action: 'CUSTOMER_UNBLOCKED',
            entityType: 'Customer',
            entityId: customer.id,
            newValue: { isBlocked: false },
        }));
        this.logger.log(`Customer ${id} unblocked`);
        return updated;
    }
    async getCustomerDevices(customerId) {
        await this.customerRepo.findOneOrFail({ where: { id: customerId } });
        return this.customerDeviceRepo.find({
            where: { customerId },
            order: { lastSeenAt: 'DESC' },
        });
    }
    async removeDevice(customerId, deviceId) {
        const device = await this.customerDeviceRepo.findOne({
            where: { id: deviceId, customerId },
        });
        if (!device) {
            throw new common_1.NotFoundException(`Device ${deviceId} not found for customer`);
        }
        device.isBlocked = true;
        await this.customerDeviceRepo.save(device);
        await this.auditLogRepo.save(this.auditLogRepo.create({
            action: 'CUSTOMER_DEVICE_REMOVED',
            entityType: 'CustomerDevice',
            entityId: device.id,
            newValue: { isBlocked: true, macAddress: device.macAddress },
        }));
        this.logger.log(`Device ${deviceId} removed from customer ${customerId}`);
    }
    async getCustomerSessions(customerId) {
        await this.customerRepo.findOneOrFail({ where: { id: customerId } });
        return this.wifiSessionRepo.find({
            where: { customerId },
            relations: ['package', 'router', 'device'],
            order: { createdAt: 'DESC' },
        });
    }
    async getCustomerPayments(customerId) {
        await this.customerRepo.findOneOrFail({ where: { id: customerId } });
        return this.paymentRepo.find({
            where: { customerId },
            relations: ['package', 'business'],
            order: { createdAt: 'DESC' },
        });
    }
    async getCustomerStats(customerId) {
        const customer = await this.customerRepo.findOneOrFail({
            where: { id: customerId },
        });
        const sessions = await this.wifiSessionRepo.find({
            where: { customerId },
            relations: ['package'],
        });
        const payments = await this.paymentRepo.find({
            where: { customerId },
        });
        const firstVisit = sessions.length > 0
            ? sessions.reduce((earliest, s) => s.startTime && s.startTime < (earliest?.startTime || s.startTime) ? s : earliest).startTime || null
            : null;
        const lastVisit = customer.lastConnectionAt || null;
        const totalDataUsed = sessions.reduce((sum, s) => sum + Number(s.dataUsedMb), 0);
        const packageCounts = new Map();
        for (const s of sessions) {
            if (s.package?.name) {
                packageCounts.set(s.package.name, (packageCounts.get(s.package.name) || 0) + 1);
            }
        }
        let favoritePackage = null;
        let maxCount = 0;
        packageCounts.forEach((count, name) => {
            if (count > maxCount) {
                maxCount = count;
                favoritePackage = name;
            }
        });
        return {
            totalPayments: payments.length,
            totalSessions: sessions.length,
            totalDataUsed,
            firstVisit,
            lastVisit,
            favoritePackage,
        };
    }
    async disconnectOldDevices(customerId, keepDevices) {
        const devices = await this.customerDeviceRepo.find({
            where: { customerId, isBlocked: false },
            order: { lastSeenAt: 'DESC' },
        });
        if (devices.length <= keepDevices) {
            return 0;
        }
        const toRemove = devices.slice(keepDevices);
        let removed = 0;
        for (const device of toRemove) {
            device.isBlocked = true;
            await this.customerDeviceRepo.save(device);
            removed++;
        }
        this.logger.log(`Disconnected ${removed} old devices for customer ${customerId}`);
        return removed;
    }
    async getCustomersStats(businessId) {
        const todayStart = new Date();
        todayStart.setHours(0, 0, 0, 0);
        const total = await this.customerRepo.count({ where: { businessId } });
        const todayNew = await this.customerRepo.count({
            where: { businessId, createdAt: (0, typeorm_2.MoreThanOrEqual)(todayStart) },
        });
        const activeToday = await this.customerRepo.count({
            where: { businessId, lastConnectionAt: (0, typeorm_2.MoreThanOrEqual)(todayStart) },
        });
        const blocked = await this.customerRepo.count({
            where: { businessId, isBlocked: true },
        });
        const payments = await this.paymentRepo.find({
            where: { businessId, status: payment_entity_1.PaymentStatus.SUCCESSFUL },
        });
        const totalRevenue = payments.reduce((sum, p) => sum + Number(p.amount), 0);
        const averageRevenuePerCustomer = total > 0 ? totalRevenue / total : 0;
        return {
            total,
            todayNew,
            activeToday,
            blocked,
            averageRevenuePerCustomer,
        };
    }
};
exports.CustomersService = CustomersService;
exports.CustomersService = CustomersService = CustomersService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(customer_entity_1.Customer)),
    __param(1, (0, typeorm_1.InjectRepository)(customer_device_entity_1.CustomerDevice)),
    __param(2, (0, typeorm_1.InjectRepository)(wifi_session_entity_1.WifiSession)),
    __param(3, (0, typeorm_1.InjectRepository)(payment_entity_1.Payment)),
    __param(4, (0, typeorm_1.InjectRepository)(voucher_entity_1.Voucher)),
    __param(5, (0, typeorm_1.InjectRepository)(audit_log_entity_1.AuditLog)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository])
], CustomersService);
