"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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DevicesService = void 0;
const common_1 = require("@nestjs/common");
const config_1 = require("../../config");
const devices_dto_1 = require("./devices.dto");
let DevicesService = class DevicesService {
    constructor(db) {
        this.db = db;
    }
    async validateOrBindDevice(memberId, ownerId, deviceId, deviceName, clientIp) {
        if (!deviceId || !deviceId.trim()) {
            return {
                valid: false,
                message: 'Device ID is required to access attendance.',
            };
        }
        const cleanDeviceId = deviceId.trim();
        const deviceBoundToOther = await this.db.teacher_devices.findFirst({
            where: {
                owner_id: ownerId,
                device_id: cleanDeviceId,
            },
            include: {
                member: true,
            },
        });
        if (deviceBoundToOther && deviceBoundToOther.member_id !== memberId) {
            const otherName = deviceBoundToOther.member
                ? `${deviceBoundToOther.member.first_name} ${deviceBoundToOther.member.last_name || ''}`.trim()
                : 'another teacher';
            return {
                valid: false,
                message: `Proxy attendance detected: This device is already locked to ${otherName}. Attendance on behalf of other teachers is strictly prohibited.`,
            };
        }
        const existingBinding = await this.db.teacher_devices.findFirst({
            where: {
                member_id: memberId,
                owner_id: ownerId,
            },
        });
        if (!existingBinding) {
            await this.db.teacher_devices.create({
                data: {
                    member_id: memberId,
                    owner_id: ownerId,
                    device_id: cleanDeviceId,
                    device_name: deviceName ?? 'Default Device',
                    last_ip: clientIp ?? null,
                    bound_at: new Date(),
                    last_active_at: new Date(),
                },
            });
            config_1.logger.info(`Device ${cleanDeviceId} successfully bound to teacher ${memberId}`);
            return { valid: true };
        }
        if (existingBinding.device_id === cleanDeviceId) {
            await this.db.teacher_devices.update({
                where: { id: existingBinding.id },
                data: {
                    last_ip: clientIp ?? existingBinding.last_ip,
                    last_active_at: new Date(),
                    ...(deviceName ? { device_name: deviceName } : {}),
                },
            });
            return { valid: true };
        }
        return {
            valid: false,
            message: `Device mismatch: Your account is locked to a different device (${existingBinding.device_name || 'Registered Device'}). Please submit a Device Change Request to the administrator.`,
        };
    }
    async requestDeviceChange(memberId, ownerId, data) {
        try {
            const existingBinding = await this.db.teacher_devices.findFirst({
                where: { member_id: memberId, owner_id: ownerId },
            });
            const oldDeviceId = existingBinding ? existingBinding.device_id : 'UNBOUND';
            const existingPending = await this.db.device_change_requests.findFirst({
                where: {
                    member_id: memberId,
                    owner_id: ownerId,
                    status: 'pending',
                },
            });
            let requestRecord;
            if (existingPending) {
                requestRecord = await this.db.device_change_requests.update({
                    where: { id: existingPending.id },
                    data: {
                        old_device_id: oldDeviceId,
                        new_device_id: data.device_id.trim(),
                        new_device_name: data.device_name ?? null,
                        reason: data.reason.trim(),
                        status: 'pending',
                    },
                });
            }
            else {
                requestRecord = await this.db.device_change_requests.create({
                    data: {
                        member_id: memberId,
                        owner_id: ownerId,
                        old_device_id: oldDeviceId,
                        new_device_id: data.device_id.trim(),
                        new_device_name: data.device_name ?? null,
                        reason: data.reason.trim(),
                        status: 'pending',
                    },
                });
            }
            const { data: parsed, error } = devices_dto_1.deviceChangeRequestResponseSchema.safeParse(requestRecord);
            if (error) {
                throw new config_1.ErrorResponse(error.message, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
            }
            return {
                status: common_1.HttpStatus.CREATED,
                message: 'Device change request submitted successfully. Please wait for admin approval.',
                data: parsed,
            };
        }
        catch (error) {
            config_1.logger.error('Error in requestDeviceChange', error);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw new config_1.ErrorResponse(error, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    async getMyDevice(memberId, ownerId) {
        try {
            const binding = await this.db.teacher_devices.findFirst({
                where: { member_id: memberId, owner_id: ownerId },
                include: { member: true },
            });
            const pendingRequest = await this.db.device_change_requests.findFirst({
                where: { member_id: memberId, owner_id: ownerId, status: 'pending' },
            });
            return {
                status: common_1.HttpStatus.OK,
                message: 'Device information retrieved successfully',
                data: {
                    binding: binding ? devices_dto_1.teacherDeviceResponseSchema.safeParse(binding).data : null,
                    pending_request: pendingRequest
                        ? devices_dto_1.deviceChangeRequestResponseSchema.safeParse(pendingRequest).data
                        : null,
                },
            };
        }
        catch (error) {
            config_1.logger.error('Error in getMyDevice', error);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw new config_1.ErrorResponse(error, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    async getDeviceChangeRequests(ownerId, filter) {
        try {
            const where = { owner_id: ownerId };
            if (filter.status && filter.status !== 'all') {
                where.status = filter.status;
            }
            if (filter.member_id) {
                where.member_id = filter.member_id;
            }
            const requests = await this.db.device_change_requests.findMany({
                where,
                include: { member: true },
                orderBy: { created_at: 'desc' },
            });
            const { data, error } = devices_dto_1.deviceChangeRequestListSchema.safeParse(requests);
            if (error) {
                throw new config_1.ErrorResponse(error.message, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
            }
            return {
                status: common_1.HttpStatus.OK,
                message: 'Device change requests retrieved successfully',
                data,
            };
        }
        catch (error) {
            config_1.logger.error('Error in getDeviceChangeRequests', error);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw new config_1.ErrorResponse(error, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    async reviewDeviceChangeRequest(requestId, ownerId, reviewerId, body) {
        try {
            const request = await this.db.device_change_requests.findFirst({
                where: { id: requestId, owner_id: ownerId },
            });
            if (!request) {
                throw new config_1.ErrorResponse('Device change request not found', common_1.HttpStatus.NOT_FOUND);
            }
            if (request.status !== 'pending') {
                throw new config_1.ErrorResponse(`Request has already been ${request.status}`, common_1.HttpStatus.BAD_REQUEST);
            }
            if (body.status === 'approved') {
                const existingDevice = await this.db.teacher_devices.findFirst({
                    where: { member_id: request.member_id, owner_id: ownerId },
                });
                if (existingDevice) {
                    await this.db.teacher_devices.update({
                        where: { id: existingDevice.id },
                        data: {
                            device_id: request.new_device_id,
                            device_name: request.new_device_name ?? existingDevice.device_name,
                            bound_at: new Date(),
                            last_active_at: new Date(),
                        },
                    });
                }
                else {
                    await this.db.teacher_devices.create({
                        data: {
                            member_id: request.member_id,
                            owner_id: ownerId,
                            device_id: request.new_device_id,
                            device_name: request.new_device_name ?? 'New Device',
                            bound_at: new Date(),
                            last_active_at: new Date(),
                        },
                    });
                }
            }
            const updatedRequest = await this.db.device_change_requests.update({
                where: { id: requestId },
                data: {
                    status: body.status,
                    reviewed_by_id: reviewerId,
                    reviewed_at: new Date(),
                    admin_note: body.admin_note ?? null,
                },
            });
            return {
                status: common_1.HttpStatus.OK,
                message: `Device change request ${body.status} successfully`,
                data: devices_dto_1.deviceChangeRequestResponseSchema.safeParse(updatedRequest).data,
            };
        }
        catch (error) {
            config_1.logger.error('Error in reviewDeviceChangeRequest', error);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw new config_1.ErrorResponse(error, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    async resetDeviceBinding(memberId, ownerId) {
        try {
            const binding = await this.db.teacher_devices.findFirst({
                where: { member_id: memberId, owner_id: ownerId },
            });
            if (!binding) {
                throw new config_1.ErrorResponse('No device binding found for this member', common_1.HttpStatus.NOT_FOUND);
            }
            await this.db.teacher_devices.delete({
                where: { id: binding.id },
            });
            return {
                status: common_1.HttpStatus.OK,
                message: 'Device binding reset successfully. Teacher can now connect with a new device.',
            };
        }
        catch (error) {
            config_1.logger.error('Error in resetDeviceBinding', error);
            if (error instanceof config_1.ErrorResponse)
                throw error;
            throw new config_1.ErrorResponse(error, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
};
exports.DevicesService = DevicesService;
exports.DevicesService = DevicesService = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [config_1.DatabaseService])
], DevicesService);
//# sourceMappingURL=devices.service.js.map