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); }
};
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { Module, Controller, Get, Post, Body, Param, Patch, Delete, ParseUUIDPipe, NotFoundException, BadRequestException, UnauthorizedException, UseGuards, Req, Query, ForbiddenException } from '@nestjs/common';
import { PrismaService } from './prisma.service.js';
import 'dotenv/config';
import * as bcrypt from 'bcrypt';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { JwtAuthGuard } from './jwt-auth.guard.js';
import { JwtStrategy } from './jwt.strategy.js';
import { TourRoleGuard } from './rbac.middleware.js';
let AppController = class AppController {
    getHello() {
        return 'Travel Planning API is running!';
    }
};
__decorate([
    Get(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", String)
], AppController.prototype, "getHello", null);
AppController = __decorate([
    Controller()
], AppController);
let AuthController = class AuthController {
    constructor(prisma, jwtService) {
        this.prisma = prisma;
        this.jwtService = jwtService;
    }
    async getStatus() {
        const userCount = await this.prisma.user.count();
        console.log(`[Status Check] Users found: ${userCount}`);
        return { isInitialSetup: userCount === 0 };
    }
    async login(body) {
        const { email, password } = body;
        const user = await this.prisma.user.findUnique({ where: { email } });
        if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
            throw new UnauthorizedException('Email hoặc mật khẩu không chính xác');
        }
        if (user.isBlocked) {
            throw new UnauthorizedException('Tài khoản của bạn đã bị khóa. Vui lòng liên hệ quản trị viên.');
        }
        const payload = { email: user.email, sub: user.id };
        return {
            access_token: this.jwtService.sign(payload),
            user: {
                id: user.id,
                email: user.email,
                name: user.name,
                isAdmin: user.isAdmin,
            },
        };
    }
    async signup(body) {
        const { email, password, name, phone, address } = body;
        const existingUser = await this.prisma.user.findUnique({ where: { email } });
        if (existingUser)
            throw new BadRequestException('Email đã được sử dụng');
        const userCount = await this.prisma.user.count();
        const shouldBeAdmin = userCount === 0;
        const passwordHash = await bcrypt.hash(password, 10);
        return this.prisma.user.create({
            data: { email, passwordHash, name, phone, address, isAdmin: shouldBeAdmin },
            select: { id: true, email: true, name: true, phone: true, address: true, isAdmin: true }
        });
    }
};
__decorate([
    Get('status'),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], AuthController.prototype, "getStatus", null);
__decorate([
    Post('login'),
    __param(0, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], AuthController.prototype, "login", null);
__decorate([
    Post('signup'),
    __param(0, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], AuthController.prototype, "signup", null);
AuthController = __decorate([
    Controller('v1/auth'),
    __metadata("design:paramtypes", [PrismaService, JwtService])
], AuthController);
let TourController = class TourController {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async createTour(body, req) {
        const { title, startDate, endDate } = body;
        return this.prisma.tour.create({
            data: {
                title,
                startDate: startDate ? new Date(startDate) : null,
                endDate: endDate ? new Date(endDate) : null,
                createdById: req.user.id,
                participants: {
                    create: {
                        userId: req.user.id,
                        role: 'OWNER'
                    }
                },
                legs: {
                    create: {
                        sequence: 1,
                        note: 'Chặng khởi đầu'
                    }
                }
            },
            include: {
                participants: {
                    include: { user: { select: { id: true, name: true, email: true } } }
                }
            }
        });
    }
    async addLocation(tourId, body, req) {
        const legId = body.legId;
        console.log(`[USER ACTION] User ${req.user.id} added a NEW VISIT point to Tour ${tourId}: "${body.name}"`);
        const leg = legId
            ? await this.prisma.leg.findUnique({ where: { id: legId } })
            : await this.prisma.leg.findFirst({ where: { tourId } });
        if (!leg)
            throw new NotFoundException('Không tìm thấy chặng phù hợp để thêm địa điểm');
        return this.prisma.location.create({
            data: {
                name: body.name,
                address: body.address,
                latitude: body.latitude,
                longitude: body.longitude,
                type: body.type,
                legId: leg.id,
                plannedStart: body.plannedStart ? new Date(body.plannedStart) : null,
                plannedEnd: body.plannedEnd ? new Date(body.plannedEnd) : null,
            }
        }).then(async (loc) => {
            if (body.expenseAmount && Number(body.expenseAmount) > 0) {
                await this.prisma.expense.create({
                    data: {
                        amount: Number(body.expenseAmount),
                        category: body.expenseCategory || 'OTHER',
                        locationId: loc.id,
                        legId: loc.legId,
                        description: body.expenseDescription || `Chi phí tại ${loc.name}`,
                        note: body.expenseNote || null,
                        paidById: body.paidById || null,
                    }
                });
            }
            return loc;
        });
    }
    async updateTourStartPoint(tourId, body, req) {
        const { latitude, longitude, name } = body;
        console.log(`[USER ACTION] User ${req.user.id} set START point for Tour ${tourId}: "${name}" at [${latitude}, ${longitude}]`);
        await this.prisma.location.deleteMany({
            where: {
                leg: { tourId: tourId },
                plannedStart: new Date(0)
            }
        });
        const firstLeg = await this.prisma.leg.findFirst({
            where: { tourId },
            orderBy: { sequence: 'asc' }
        });
        if (!firstLeg)
            throw new NotFoundException('Không tìm thấy chặng đầu tiên cho Tour này');
        return this.prisma.location.create({
            data: {
                name: name || 'Điểm xuất phát',
                latitude,
                longitude,
                type: 'MOVE',
                legId: firstLeg.id,
                plannedStart: new Date(0),
            }
        });
    }
    async updateTourEndPoint(tourId, body, req) {
        const { latitude, longitude, name } = body;
        console.log(`[USER ACTION] User ${req.user.id} set END point for Tour ${tourId}: "${name}" at [${latitude}, ${longitude}]`);
        await this.prisma.location.deleteMany({
            where: {
                leg: { tourId: tourId },
                plannedEnd: new Date(0)
            }
        });
        const lastLeg = await this.prisma.leg.findFirst({
            where: { tourId },
            orderBy: { sequence: 'desc' }
        });
        if (!lastLeg)
            throw new NotFoundException('Không tìm thấy chặng cuối cùng cho Tour này');
        return this.prisma.location.create({
            data: {
                name: name || 'Điểm kết thúc',
                latitude,
                longitude,
                type: 'MOVE',
                legId: lastLeg.id,
                plannedEnd: new Date(0),
            }
        });
    }
    async initializeLegs(tourId, body) {
        const { count } = body;
        if (count <= 0 || count > 20)
            throw new BadRequestException('Số lượng chặng không hợp lệ (1-20)');
        const existingLegs = await this.prisma.leg.findMany({
            where: { tourId },
            orderBy: { sequence: 'asc' }
        });
        const needed = count - existingLegs.length;
        if (needed > 0) {
            const createData = Array.from({ length: needed }).map((_, i) => ({
                tourId,
                sequence: existingLegs.length + i + 1,
                note: `Chặng ${existingLegs.length + i + 1}`
            }));
            await this.prisma.leg.createMany({ data: createData });
        }
        const allLegs = await this.prisma.leg.findMany({
            where: { tourId },
            orderBy: { sequence: 'asc' }
        });
        const lastLeg = allLegs[allLegs.length - 1];
        const endPoint = await this.prisma.location.findFirst({
            where: { leg: { tourId }, plannedEnd: new Date(0) }
        });
        if (endPoint && lastLeg && endPoint.legId !== lastLeg.id) {
            await this.prisma.location.update({
                where: { id: endPoint.id },
                data: { legId: lastLeg.id }
            });
        }
        return allLegs;
    }
    async addLeg(tourId, body) {
        const tour = await this.prisma.tour.findUnique({
            where: { id: tourId },
            include: { legs: true }
        });
        if (!tour)
            throw new NotFoundException('Không tìm thấy tour');
        return this.prisma.leg.create({
            data: {
                tourId,
                sequence: tour.legs.length + 1,
                note: body.note || `Chặng ${tour.legs.length + 1}`
            }
        });
    }
    async updateTour(id, body) {
        return this.prisma.tour.update({
            where: { id },
            data: {
                title: body.title,
                startDate: body.startDate ? new Date(body.startDate) : undefined,
                endDate: body.endDate ? new Date(body.endDate) : undefined,
            },
        });
    }
    async deleteTour(id) {
        await this.prisma.tour.delete({
            where: { id },
        });
        return { success: true };
    }
    async getPublicTours(req) {
        return this.prisma.tour.findMany({
            where: {
                participants: {
                    some: { userId: req.user.id }
                }
            },
            take: 20,
            include: {
                photos: { take: 1 },
                legs: {
                    orderBy: { sequence: 'asc' },
                    include: {
                        locations: { orderBy: { plannedStart: 'asc' } }
                    }
                }
            }
        });
    }
    async getTourDetails(id) {
        const tour = await this.prisma.tour.findUnique({
            where: { id },
            include: {
                participants: {
                    include: {
                        user: {
                            select: { id: true, name: true, email: true }
                        }
                    }
                },
                photos: true,
                legs: {
                    orderBy: { sequence: 'asc' },
                    include: {
                        locations: { orderBy: { plannedStart: 'asc' } },
                    },
                },
            },
        });
        if (!tour)
            throw new NotFoundException(`Không tìm thấy Tour với ID ${id}`);
        return tour;
    }
    async addMember(tourId, body, req) {
        const validRoles = ['OWNER', 'MANAGER', 'MEMBER', 'MEMBER_NO_FINANCE', 'VIEWER_ONLY'];
        const role = validRoles.includes(body.role) ? body.role : 'MEMBER';
        const participation = await this.prisma.tourParticipant.findUnique({
            where: { tourId_userId: { tourId, userId: body.userId } },
        });
        if (participation) {
            return this.prisma.tourParticipant.update({
                where: { tourId_userId: { tourId, userId: body.userId } },
                data: { role },
                include: { user: { select: { id: true, name: true, email: true } } },
            });
        }
        let currentRole = req.user.tourParticipation?.role;
        if (!currentRole) {
            const participation = await this.prisma.tourParticipant.findUnique({
                where: { tourId_userId: { tourId, userId: req.user.id } },
            });
            currentRole = participation?.role;
        }
        if (!currentRole || !(currentRole === 'OWNER' || currentRole === 'MANAGER')) {
            const joinRequest = await this.prisma.joinRequest.create({
                data: {
                    tourId,
                    userId: body.userId,
                    requestedById: req.user.id,
                    status: 'PENDING',
                },
                include: {
                    user: { select: { id: true, name: true, email: true } },
                    requestedBy: { select: { id: true, name: true, email: true } },
                },
            });
            return { ...joinRequest, pendingApproval: true };
        }
        return this.prisma.tourParticipant.create({
            data: {
                tourId,
                userId: body.userId,
                role,
            },
            include: { user: { select: { id: true, name: true, email: true } } },
        });
    }
    async getJoinRequests(tourId, req) {
        const requests = await this.prisma.joinRequest.findMany({
            where: { tourId, status: 'PENDING' },
            orderBy: { createdAt: 'desc' },
            include: {
                user: { select: { id: true, name: true, email: true } },
                requestedBy: { select: { id: true, name: true, email: true } },
            },
        });
        return requests;
    }
    async createJoinRequest(tourId, body, req) {
        const requestingUserId = body.userId || req.user.id;
        const existingParticipation = await this.prisma.tourParticipant.findUnique({
            where: { tourId_userId: { tourId, userId: requestingUserId } },
        });
        if (existingParticipation) {
            throw new BadRequestException('Người dùng này đã là thành viên của tour.');
        }
        const pendingRequest = await this.prisma.joinRequest.findFirst({
            where: { tourId, userId: requestingUserId, status: 'PENDING' },
        });
        if (pendingRequest) {
            return pendingRequest;
        }
        const joinRequest = await this.prisma.joinRequest.create({
            data: {
                tourId,
                userId: requestingUserId,
                requestedById: req.user.id,
                status: 'PENDING',
            },
            include: {
                user: { select: { id: true, name: true, email: true } },
                requestedBy: { select: { id: true, name: true, email: true } },
            },
        });
        return joinRequest;
    }
    async acceptJoinRequest(tourId, requestId, req) {
        let role = req.user.tourParticipation?.role;
        if (!role) {
            const participation = await this.prisma.tourParticipant.findUnique({
                where: { tourId_userId: { tourId, userId: req.user.id } },
            });
            role = participation?.role;
        }
        if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
            throw new ForbiddenException('Bạn không có quyền chấp nhận yêu cầu tham gia.');
        }
        const joinRequest = await this.prisma.joinRequest.findUnique({
            where: { id: requestId },
        });
        if (!joinRequest || joinRequest.tourId !== tourId) {
            throw new NotFoundException('Không tìm thấy yêu cầu tham gia.');
        }
        if (joinRequest.status !== 'PENDING') {
            throw new BadRequestException('Yêu cầu này đã được xử lý trước đó.');
        }
        const existing = await this.prisma.tourParticipant.findUnique({
            where: { tourId_userId: { tourId, userId: joinRequest.userId } },
        });
        if (existing) {
            await this.prisma.joinRequest.update({
                where: { id: requestId },
                data: { status: 'REJECTED' },
            });
            return { success: true, message: 'Người dùng đã là thành viên, yêu cầu đã bị từ chối.' };
        }
        await this.prisma.$transaction([
            this.prisma.tourParticipant.create({
                data: {
                    tourId,
                    userId: joinRequest.userId,
                    role: 'MEMBER',
                },
            }),
            this.prisma.joinRequest.update({
                where: { id: requestId },
                data: { status: 'ACCEPTED' },
            }),
        ]);
        return { success: true, message: 'Đã chấp nhận yêu cầu tham gia.' };
    }
    async rejectJoinRequest(tourId, requestId, req) {
        let role = req.user.tourParticipation?.role;
        if (!role) {
            const participation = await this.prisma.tourParticipant.findUnique({
                where: { tourId_userId: { tourId, userId: req.user.id } },
            });
            role = participation?.role;
        }
        if (!role || !(role === 'OWNER' || role === 'MANAGER')) {
            throw new ForbiddenException('Bạn không có quyền từ chối yêu cầu tham gia.');
        }
        const joinRequest = await this.prisma.joinRequest.findUnique({
            where: { id: requestId },
        });
        if (!joinRequest || joinRequest.tourId !== tourId) {
            throw new NotFoundException('Không tìm thấy yêu cầu tham gia.');
        }
        if (joinRequest.status !== 'PENDING') {
            throw new BadRequestException('Yêu cầu này đã được xử lý trước đó.');
        }
        await this.prisma.joinRequest.update({
            where: { id: requestId },
            data: { status: 'REJECTED' },
        });
        return { success: true, message: 'Đã từ chối yêu cầu tham gia.' };
    }
    async removeMember(tourId, userId) {
        const participation = await this.prisma.tourParticipant.findUnique({
            where: { tourId_userId: { tourId, userId } },
        });
        if (!participation) {
            throw new NotFoundException('Thành viên này không có trong tour');
        }
        await this.prisma.tourParticipant.delete({
            where: { tourId_userId: { tourId, userId } },
        });
        return { message: 'Đã xóa thành viên khỏi tour' };
    }
};
__decorate([
    UseGuards(JwtAuthGuard),
    Post(),
    __param(0, Body()),
    __param(1, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "createTour", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/locations'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "addLocation", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/start-point'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "updateTourStartPoint", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/end-point'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "updateTourEndPoint", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/legs/batch'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "initializeLegs", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/legs'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "addLeg", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Patch(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "updateTour", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Delete(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "deleteTour", null);
__decorate([
    UseGuards(JwtAuthGuard),
    Get('explore'),
    __param(0, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "getPublicTours", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Get(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "getTourDetails", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/members'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "addMember", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Get(':tourId/join-requests'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "getJoinRequests", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/join-requests'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Body()),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "createJoinRequest", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/join-requests/:requestId/accept'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Param('requestId')),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "acceptJoinRequest", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Post(':tourId/join-requests/:requestId/reject'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Param('requestId')),
    __param(2, Req()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String, Object]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "rejectJoinRequest", null);
__decorate([
    UseGuards(JwtAuthGuard, TourRoleGuard),
    Delete(':tourId/members/:userId'),
    __param(0, Param('tourId', ParseUUIDPipe)),
    __param(1, Param('userId', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, String]),
    __metadata("design:returntype", Promise)
], TourController.prototype, "removeMember", null);
TourController = __decorate([
    Controller('v1/tours'),
    __metadata("design:paramtypes", [PrismaService])
], TourController);
let LocationController = class LocationController {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async updateLocation(id, body) {
        const { expenseAmount, expenseCategory, ...data } = body;
        const location = await this.prisma.location.update({
            where: { id },
            data: {
                name: data.name,
                address: data.address,
                latitude: data.latitude,
                longitude: data.longitude,
                type: data.type,
                plannedStart: data.plannedStart ? new Date(data.plannedStart) : undefined,
                plannedEnd: data.plannedEnd ? new Date(data.plannedEnd) : undefined,
                status: data.status,
            }
        });
        if (expenseAmount !== undefined) {
            const amount = Number(expenseAmount);
            const existingExpense = await this.prisma.expense.findFirst({
                where: { locationId: id }
            });
            if (existingExpense) {
                await this.prisma.expense.update({
                    where: { id: existingExpense.id },
                    data: { amount, category: expenseCategory || 'OTHER' }
                });
            }
            else if (amount > 0) {
                await this.prisma.expense.create({
                    data: {
                        amount,
                        category: expenseCategory || 'OTHER',
                        locationId: id,
                        legId: location.legId,
                        description: `Chi phí tại ${location.name}`
                    }
                });
            }
        }
        return location;
    }
    async deleteLocation(id) {
        await this.prisma.location.delete({ where: { id } });
        return { success: true };
    }
};
__decorate([
    Patch(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], LocationController.prototype, "updateLocation", null);
__decorate([
    Delete(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], LocationController.prototype, "deleteLocation", null);
LocationController = __decorate([
    Controller('v1/locations'),
    UseGuards(JwtAuthGuard),
    __metadata("design:paramtypes", [PrismaService])
], LocationController);
let LegController = class LegController {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async updateLeg(id, body) {
        return this.prisma.leg.update({
            where: { id },
            data: {
                note: body.note,
                sequence: body.sequence
            }
        });
    }
    async deleteLeg(id) {
        const leg = await this.prisma.leg.findUnique({
            where: { id },
            include: { _count: { select: { locations: true } } }
        });
        if (leg?._count.locations && leg._count.locations > 0) {
            throw new BadRequestException('Không thể xóa chặng đang có địa điểm. Hãy xóa địa điểm trước.');
        }
        await this.prisma.leg.delete({ where: { id } });
        return { success: true };
    }
};
__decorate([
    Patch(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], LegController.prototype, "updateLeg", null);
__decorate([
    Delete(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], LegController.prototype, "deleteLeg", null);
LegController = __decorate([
    Controller('v1/legs'),
    UseGuards(JwtAuthGuard),
    __metadata("design:paramtypes", [PrismaService])
], LegController);
function calculateDistance(lat1, lon1, lat2, lon2) {
    const p = 0.017453292519943295;
    const c = Math.cos;
    const a = 0.5 - c((lat2 - lat1) * p) / 2 +
        c(lat1 * p) * c(lat2 * p) *
            (1 - c((lon2 - lon1) * p)) / 2;
    return 12742 * Math.asin(Math.sqrt(a));
}
let RoutingController = class RoutingController {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async optimize(legId) {
        const currentLeg = await this.prisma.leg.findUnique({
            where: { id: legId },
        });
        if (!currentLeg)
            throw new NotFoundException('Không tìm thấy chặng');
        const locations = await this.prisma.location.findMany({
            where: { legId },
        });
        if (locations.length === 0)
            return { locations: [], totalDistance: 0 };
        let startAnchor = null;
        const prevLeg = await this.prisma.leg.findFirst({
            where: {
                tourId: currentLeg.tourId,
                sequence: currentLeg.sequence - 1
            },
            include: { locations: { orderBy: { plannedStart: 'asc' } } }
        });
        if (prevLeg?.locations?.length) {
            startAnchor = prevLeg.locations[prevLeg.locations.length - 1];
        }
        if (locations.length <= 2 && !startAnchor)
            return { locations, totalDistance: 0 };
        const optimized = [];
        const unvisited = [...locations];
        let current;
        if (startAnchor) {
            let nearestIdx = 0;
            let minDist = Infinity;
            for (let i = 0; i < unvisited.length; i++) {
                const d = calculateDistance(startAnchor.latitude, startAnchor.longitude, unvisited[i].latitude, unvisited[i].longitude);
                if (d < minDist) {
                    minDist = d;
                    nearestIdx = i;
                }
            }
            current = unvisited.splice(nearestIdx, 1)[0];
        }
        else {
            current = unvisited.sort((a, b) => (a.plannedStart?.getTime() || 0) - (b.plannedStart?.getTime() || 0)).shift();
        }
        optimized.push(current);
        while (unvisited.length > 0) {
            let nearestIdx = 0;
            let minDist = Infinity;
            for (let i = 0; i < unvisited.length; i++) {
                const d = calculateDistance(current.latitude, current.longitude, unvisited[i].latitude, unvisited[i].longitude);
                if (d < minDist) {
                    minDist = d;
                    nearestIdx = i;
                }
            }
            current = unvisited.splice(nearestIdx, 1)[0];
            optimized.push(current);
        }
        let totalDistance = 0;
        if (startAnchor) {
            totalDistance += calculateDistance(startAnchor.latitude, startAnchor.longitude, optimized[0].latitude, optimized[0].longitude);
        }
        for (let i = 0; i < optimized.length - 1; i++) {
            totalDistance += calculateDistance(optimized[i].latitude, optimized[i].longitude, optimized[i + 1].latitude, optimized[i + 1].longitude);
        }
        const baseTime = optimized[0].plannedStart || new Date();
        await Promise.all(optimized.map((loc, index) => this.prisma.location.update({
            where: { id: loc.id },
            data: { plannedStart: new Date(baseTime.getTime() + index * 3600000) },
        })));
        const updatedLocations = await this.prisma.location.findMany({
            where: { legId },
            orderBy: { plannedStart: 'asc' }
        });
        return {
            locations: updatedLocations,
            totalDistance: parseFloat(totalDistance.toFixed(2))
        };
    }
};
__decorate([
    Post('optimize/:legId'),
    __param(0, Param('legId', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], RoutingController.prototype, "optimize", null);
RoutingController = __decorate([
    Controller('v1/routing'),
    __metadata("design:paramtypes", [PrismaService])
], RoutingController);
let UserController = class UserController {
    constructor(prisma) {
        this.prisma = prisma;
    }
    async getAllUsers(req, q) {
        const currentUserId = req.user?.sub;
        const users = await this.prisma.user.findMany({
            where: q
                ? {
                    OR: [
                        { name: { contains: q, mode: 'insensitive' } },
                        { email: { contains: q, mode: 'insensitive' } },
                    ],
                }
                : undefined,
            select: { id: true, email: true, name: true, isAdmin: true, isBlocked: true, createdAt: true, phone: true, address: true }
        });
        return users.filter((u) => u.id !== currentUserId);
    }
    async updateUser(id, data) {
        if (data.password) {
            data.passwordHash = await bcrypt.hash(data.password, 10);
            delete data.password;
        }
        return this.prisma.user.update({
            where: { id },
            data,
            select: { id: true, email: true, name: true, isAdmin: true, isBlocked: true }
        });
    }
    async deleteUser(id) {
        const user = await this.prisma.user.findUnique({ where: { id } });
        if (!user)
            throw new NotFoundException('Không tìm thấy người dùng');
        if (user.isAdmin) {
            const adminCount = await this.prisma.user.count({ where: { isAdmin: true } });
            if (adminCount <= 1)
                throw new BadRequestException('Không thể xóa Quản trị viên cuối cùng');
        }
        await this.prisma.tourParticipant.deleteMany({ where: { userId: id } });
        await this.prisma.user.delete({ where: { id } });
        return { message: 'Đã xóa người dùng' };
    }
    async toggleBlock(id) {
        const user = await this.prisma.user.findUnique({ where: { id } });
        if (!user)
            throw new NotFoundException('Người dùng không tồn tại');
        const updated = await this.prisma.user.update({
            where: { id },
            data: { isBlocked: !user.isBlocked },
            select: { id: true, email: true, name: true, isBlocked: true }
        });
        return updated;
    }
};
__decorate([
    Get(),
    __param(0, Req()),
    __param(1, Query('q')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, String]),
    __metadata("design:returntype", Promise)
], UserController.prototype, "getAllUsers", null);
__decorate([
    Patch(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __param(1, Body()),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Object]),
    __metadata("design:returntype", Promise)
], UserController.prototype, "updateUser", null);
__decorate([
    Delete(':id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], UserController.prototype, "deleteUser", null);
__decorate([
    Post('block/:id'),
    __param(0, Param('id', ParseUUIDPipe)),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Promise)
], UserController.prototype, "toggleBlock", null);
UserController = __decorate([
    Controller('v1/users'),
    __metadata("design:paramtypes", [PrismaService])
], UserController);
let AppModule = class AppModule {
};
AppModule = __decorate([
    Module({
        imports: [
            JwtModule.register({
                secret: process.env.JWT_SECRET || 'super-secret',
                signOptions: { expiresIn: '1d' },
            }),
        ],
        controllers: [AppController, AuthController, TourController, UserController, RoutingController, LegController, LocationController],
        providers: [PrismaService, JwtStrategy, TourRoleGuard],
        exports: [PrismaService]
    })
], AppModule);
async function bootstrap() {
    const app = await NestFactory.create(AppModule);
    app.enableCors();
    app.setGlobalPrefix('api');
    const port = process.env.PORT || 3001;
    await app.listen(port);
    console.log(`🚀 Server is running on: http://localhost:${port}`);
}
bootstrap();
//# sourceMappingURL=main.js.map