"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
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 __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
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.StudentsController = void 0;
const common_1 = require("@nestjs/common");
const students_service_1 = require("./students.service");
const create_student_dto_1 = require("./dto/create-student.dto");
const update_student_dto_1 = require("./dto/update-student.dto");
const promote_student_dto_1 = require("./dto/promote-student.dto");
const re_admit_student_dto_1 = require("./dto/re-admit-student.dto");
const jwt_auth_guard_1 = require("../common/guards/jwt-auth.guard");
const roles_guard_1 = require("../common/guards/roles.guard");
const roles_decorator_1 = require("../common/decorators/roles.decorator");
const role_enum_1 = require("../common/roles/role.enum");
const current_user_decorator_1 = require("../common/decorators/current-user.decorator");
const mongo_id_pipe_1 = require("../common/pipes/mongo-id.pipe");
const bcrypt = __importStar(require("bcryptjs"));
const url_helper_1 = require("../common/helpers/url.helper");
const BCRYPT_ROUNDS = Number(process.env.BCRYPT_ROUNDS || 10);
const platform_express_1 = require("@nestjs/platform-express");
const multer_1 = require("multer");
const path_1 = require("path");
const fs = __importStar(require("fs"));
const mongoose_1 = require("@nestjs/mongoose");
const mongoose_2 = require("mongoose");
const section_schema_1 = require("../sections/schemas/section.schema");
const promote_bulk_dto_1 = require("./dto/promote-bulk.dto");
const ensureDir = (dir) => {
    if (!fs.existsSync(dir))
        fs.mkdirSync(dir, { recursive: true });
};
const storage = (0, multer_1.diskStorage)({
    destination: (_req, _file, cb) => {
        const dir = (0, path_1.join)(process.cwd(), 'uploads', 'students');
        ensureDir(dir);
        cb(null, dir);
    },
    filename: (_req, file, cb) => {
        const name = Date.now() + '-' + Math.random().toString(36).slice(2);
        cb(null, name + (0, path_1.extname)(file.originalname));
    },
});
const fileFilter = (_req, file, cb) => {
    const ok = /(jpg|jpeg|png|pdf)$/i.test((0, path_1.extname)(file.originalname));
    if (!ok)
        return cb(new common_1.BadRequestException('Only JPG, PNG, or PDF files allowed'), false);
    cb(null, true);
};
const norm = (v) => {
    const s = (v ?? '').trim();
    return s.length ? s : undefined;
};
let StudentsController = class StudentsController {
    studentsService;
    sectionModel;
    constructor(studentsService, sectionModel) {
        this.studentsService = studentsService;
        this.sectionModel = sectionModel;
    }
    create(dto) {
        const documents = {
            photoUrl: dto.photoUrl,
            fatherPhotoUrl: dto.fatherPhotoUrl,
            motherPhotoUrl: dto.motherPhotoUrl,
            birthCertificateUrl: dto.birthCertificateUrl,
            scstCertificateUrl: dto.scstCertificateUrl,
            leavingCertificateUrl: dto.leavingCertificateUrl,
            vaccineCardUrl: dto.vaccineCardUrl,
        };
        return this.studentsService.create(dto, documents);
    }
    updatePhotos(id, files) {
        const toUrl = (f) => f?.[0] ? (0, url_helper_1.toFullUrl)(`/uploads/students/${f[0].filename}`) : undefined;
        return this.studentsService.updateDocuments(id, {
            photoUrl: toUrl(files?.photo),
            fatherPhotoUrl: toUrl(files?.fatherPhoto),
            motherPhotoUrl: toUrl(files?.motherPhoto),
        });
    }
    findAll(ayId) {
        const ay = norm(ayId);
        if (ay) {
            return this.studentsService.filterStudents({ academicYearId: ay });
        }
        return this.studentsService.findAll();
    }
    filter(classId, sectionId, academicYearId, ayId) {
        const classIdN = norm(classId);
        const sectionIdN = norm(sectionId);
        const ayFromQuery = norm(academicYearId);
        const ayFromHeader = norm(ayId);
        const ay = ayFromQuery ?? ayFromHeader;
        return this.studentsService.filterStudents({
            classId: classIdN,
            sectionId: sectionIdN,
            academicYearId: ay,
        });
    }
    search(query, limit) {
        const q = norm(query);
        if (!q) {
            throw new common_1.BadRequestException('Search query is required');
        }
        const limitNum = Math.max(1, Math.min(Number(limit ?? 50) || 50, 100));
        return this.studentsService.searchStudents(q, limitNum);
    }
    getRecentEnrollments(limit, ayId) {
        const n = Math.max(1, Math.min(Number(limit ?? 5) || 5, 25));
        const ay = norm(ayId);
        return this.studentsService.getRecentEnrollments(n, ay);
    }
    getMonthlyEnrollmentStats(ayId) {
        const ay = norm(ayId);
        return this.studentsService.getMonthlyEnrollmentStats(ay);
    }
    promote(id, body) {
        return this.studentsService.promote(id, body.newClassId, body.newSectionId, body.newAcademicYearId);
    }
    promoteBulk(body, ayId) {
        return this.studentsService.promoteBulk(body, norm(ayId));
    }
    reAdmit(id, body) {
        return this.studentsService.reAdmit(id, body.newClassId, body.newSectionId, body.newAcademicYearId);
    }
    reAdmitBulk(body) {
        return this.studentsService.reAdmitBulk(body.studentIds, body.newClassId, body.newSectionId, body.newAcademicYearId);
    }
    me(userId) {
        return this.studentsService.findOne(userId);
    }
    async updateOwnPassword(userId, currentPassword, newPassword) {
        if (!currentPassword) {
            throw new common_1.BadRequestException('Current password is required');
        }
        if (!newPassword || String(newPassword).length < 8) {
            throw new common_1.BadRequestException('Password must be at least 8 characters');
        }
        const pwdRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).{8,}$/;
        if (!pwdRegex.test(newPassword)) {
            throw new common_1.BadRequestException('Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character');
        }
        const student = await this.studentsService.findByIdForAuth(userId);
        if (!student) {
            throw new common_1.BadRequestException('Student not found');
        }
        if (!student.password) {
            throw new common_1.BadRequestException('Password authentication is not set up for this account');
        }
        const isPasswordValid = await bcrypt.compare(currentPassword, student.password);
        if (!isPasswordValid) {
            throw new common_1.BadRequestException('Current password is incorrect');
        }
        const hashedPassword = await bcrypt.hash(newPassword, BCRYPT_ROUNDS);
        return this.studentsService.updatePassword(userId, hashedPassword);
    }
    async getStudentsBySectionForTeacher(sectionId, userId, role) {
        if (!sectionId)
            throw new common_1.BadRequestException('sectionId is required');
        if (String(role).toLowerCase() === role_enum_1.Role.TEACHER) {
            const sec = await this.sectionModel.findById(sectionId).lean();
            if (!sec)
                throw new common_1.BadRequestException('Section not found');
            if (String(sec.assignedTeacherId || '') !== String(userId)) {
                throw new common_1.ForbiddenException('You are not assigned to this section.');
            }
        }
        return this.studentsService.findBySection(sectionId);
    }
    async getMySectionsStudents(teacherId) {
        const sections = await this.sectionModel
            .find({ assignedTeacherId: teacherId })
            .select({ _id: 1, name: 1, classId: 1 })
            .lean();
        if (!sections.length)
            return [];
        const sectionIds = sections.map((s) => String(s._id));
        const students = await this.studentsService.findBySectionIds(sectionIds);
        const bySection = {};
        for (const s of students) {
            const sid = String(s.sectionId);
            if (!bySection[sid])
                bySection[sid] = [];
            bySection[sid].push(s);
        }
        return sections.map((s) => ({
            sectionId: String(s._id),
            name: s.name,
            classId: s.classId,
            students: bySection[String(s._id)] || [],
        }));
    }
    findOne(id, ayId) {
        const ay = norm(ayId);
        return this.studentsService.findOne(id, ay);
    }
    async toggleActive(id) {
        return this.studentsService.toggleActive(id);
    }
    async resetPassword(id, newPassword) {
        if (!newPassword || String(newPassword).length < 8) {
            throw new common_1.BadRequestException('Password must be at least 8 characters');
        }
        const pwdRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*(),.?":{}|<>]).{8,}$/;
        if (!pwdRegex.test(newPassword)) {
            throw new common_1.BadRequestException('Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character');
        }
        const hashedPassword = await bcrypt.hash(newPassword, BCRYPT_ROUNDS);
        return this.studentsService.updatePassword(id, hashedPassword);
    }
    update(id, dto) {
        return this.studentsService.update(id, dto);
    }
    remove(id) {
        return this.studentsService.delete(id);
    }
};
exports.StudentsController = StudentsController;
__decorate([
    (0, common_1.Post)(),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [create_student_dto_1.CreateStudentDto]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "create", null);
__decorate([
    (0, common_1.Put)(':id/photos'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    (0, common_1.UseInterceptors)((0, platform_express_1.FileFieldsInterceptor)([
        { name: 'photo', maxCount: 1 },
        { name: 'fatherPhoto', maxCount: 1 },
        { name: 'motherPhoto', maxCount: 1 },
    ], { storage, fileFilter, limits: { fileSize: 5 * 1024 * 1024 } })),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.UploadedFiles)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "updatePhotos", null);
__decorate([
    (0, common_1.Get)(),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "findAll", null);
__decorate([
    (0, common_1.Get)('filter'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Query)('classId')),
    __param(1, (0, common_1.Query)('sectionId')),
    __param(2, (0, common_1.Query)('academicYearId')),
    __param(3, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, String, String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "filter", null);
__decorate([
    (0, common_1.Get)('search'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Query)('q')),
    __param(1, (0, common_1.Query)('limit')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "search", null);
__decorate([
    (0, common_1.Get)('recent'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Query)('limit')),
    __param(1, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "getRecentEnrollments", null);
__decorate([
    (0, common_1.Get)('analytics/monthly'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "getMonthlyEnrollmentStats", null);
__decorate([
    (0, common_1.Put)('promote/:id'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, promote_student_dto_1.PromoteStudentDto]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "promote", null);
__decorate([
    (0, common_1.Post)('promote-bulk'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Body)()),
    __param(1, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [promote_bulk_dto_1.PromoteBulkDto, String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "promoteBulk", null);
__decorate([
    (0, common_1.Put)('re-admit/:id'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, re_admit_student_dto_1.ReAdmitStudentDto]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "reAdmit", null);
__decorate([
    (0, common_1.Post)('re-admit-bulk'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [re_admit_student_dto_1.ReAdmitBulkDto]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "reAdmitBulk", null);
__decorate([
    (0, common_1.Get)('me'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.STUDENT),
    __param(0, (0, current_user_decorator_1.CurrentUser)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "me", null);
__decorate([
    (0, common_1.Put)('me/password'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.STUDENT),
    __param(0, (0, current_user_decorator_1.CurrentUser)('id')),
    __param(1, (0, common_1.Body)('currentPassword')),
    __param(2, (0, common_1.Body)('password')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, String]),
    __metadata("design:returntype", Promise)
], StudentsController.prototype, "updateOwnPassword", null);
__decorate([
    (0, common_1.Get)('teacher/by-section'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.TEACHER, role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Query)('sectionId', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, current_user_decorator_1.CurrentUser)('id')),
    __param(2, (0, current_user_decorator_1.CurrentUser)('role')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, String]),
    __metadata("design:returntype", Promise)
], StudentsController.prototype, "getStudentsBySectionForTeacher", null);
__decorate([
    (0, common_1.Get)('teacher/my-sections'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.TEACHER),
    __param(0, (0, current_user_decorator_1.CurrentUser)('id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], StudentsController.prototype, "getMySectionsStudents", null);
__decorate([
    (0, common_1.Get)(':id'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.Headers)('x-academic-year-id')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "findOne", null);
__decorate([
    (0, common_1.Patch)(':id/toggle-active'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], StudentsController.prototype, "toggleActive", null);
__decorate([
    (0, common_1.Put)(':id/reset-password'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.Body)('password')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", Promise)
], StudentsController.prototype, "resetPassword", null);
__decorate([
    (0, common_1.Put)(':id'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __param(1, (0, common_1.Body)()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, update_student_dto_1.UpdateStudentDto]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "update", null);
__decorate([
    (0, common_1.Delete)(':id'),
    (0, roles_decorator_1.Roles)(role_enum_1.Role.ADMIN),
    __param(0, (0, common_1.Param)('id', new mongo_id_pipe_1.MongoIdPipe())),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", void 0)
], StudentsController.prototype, "remove", null);
exports.StudentsController = StudentsController = __decorate([
    (0, common_1.UseGuards)(jwt_auth_guard_1.JwtAuthGuard, roles_guard_1.RolesGuard),
    (0, common_1.Controller)('students'),
    __param(1, (0, mongoose_1.InjectModel)(section_schema_1.Section.name)),
    __metadata("design:paramtypes", [students_service_1.StudentsService,
        mongoose_2.Model])
], StudentsController);
//# sourceMappingURL=students.controller.js.map