"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.SettlementsService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const settlement_entity_1 = require("../../database/entities/settlement.entity");
const user_entity_1 = require("../../database/entities/user.entity");
const group_member_entity_1 = require("../../database/entities/group-member.entity");
const activity_service_1 = require("../activity/activity.service");
const notifications_service_1 = require("../notifications/notifications.service");
const enums_1 = require("../../common/enums");
let SettlementsService = class SettlementsService {
    constructor(settlementRepo, userRepo, groupMemberRepo, activityService, notificationsService) {
        this.settlementRepo = settlementRepo;
        this.userRepo = userRepo;
        this.groupMemberRepo = groupMemberRepo;
        this.activityService = activityService;
        this.notificationsService = notificationsService;
    }
    async createSettlement(payerId, dto) {
        if (dto.payeeId === payerId) {
            throw new common_1.BadRequestException('You cannot record a settlement with yourself');
        }
        const payee = await this.userRepo.findOne({ where: { id: dto.payeeId } });
        if (!payee)
            throw new common_1.NotFoundException('Payee user not found');
        if (dto.groupId) {
            await this.assertGroupMember(dto.groupId, payerId);
            await this.assertGroupMember(dto.groupId, dto.payeeId);
        }
        const settlementEntity = this.settlementRepo.create({
            payerId,
            payeeId: dto.payeeId,
            amount: String(dto.amount),
            currency: dto.currency ?? 'USD',
            groupId: dto.groupId,
            method: dto.method ?? enums_1.SettlementMethod.Cash,
            notes: dto.notes,
            settledAt: dto.settledAt ? new Date(dto.settledAt) : new Date(),
        });
        await this.settlementRepo.save(settlementEntity);
        const settlement = await this.settlementRepo.findOne({
            where: { id: settlementEntity.id },
            relations: { payer: true, payee: true },
        });
        if (!settlement)
            throw new common_1.NotFoundException('Settlement not found');
        void this.activityService.log({
            actorId: payerId,
            action: enums_1.ActivityAction.SettlementRecorded,
            groupId: dto.groupId,
            entityId: settlement.id,
            entityType: enums_1.EntityType.Settlement,
            metadata: { amount: Number(settlement.amount), currency: settlement.currency },
        });
        void this.notificationsService.notify({
            userId: dto.payeeId,
            title: 'Payment received',
            body: `${settlement.payer.name} recorded a payment of ${settlement.currency} ${Number(settlement.amount)} to you`,
            type: enums_1.NotificationType.Settlement,
            entityId: settlement.id,
        });
        return this.toDto(settlement);
    }
    async listMySettlements(userId, query) {
        const page = query.page ?? 1;
        const limit = query.limit ?? 20;
        const qb = this.settlementRepo
            .createQueryBuilder('settlement')
            .where('settlement.payerId = :userId OR settlement.payeeId = :userId', { userId });
        if (query.isConfirmed !== undefined) {
            qb.andWhere('settlement.isConfirmed = :isConfirmed', { isConfirmed: query.isConfirmed });
        }
        return this.paginateQuery(qb, page, limit, query.sortOrder);
    }
    async listGroupSettlements(groupId, query) {
        const page = query.page ?? 1;
        const limit = query.limit ?? 20;
        const where = { groupId };
        if (query.isConfirmed !== undefined) {
            where.isConfirmed = query.isConfirmed;
        }
        return this.paginate(where, page, limit, query.sortOrder);
    }
    async getSettlement(id, userId) {
        const settlement = await this.settlementRepo.findOne({
            where: { id },
            relations: { payer: true, payee: true },
        });
        if (!settlement)
            throw new common_1.NotFoundException('Settlement not found');
        if (settlement.payerId !== userId && settlement.payeeId !== userId) {
            throw new common_1.ForbiddenException('You do not have access to this settlement');
        }
        return this.toDto(settlement);
    }
    async confirmSettlement(id, userId) {
        const settlement = await this.settlementRepo.findOne({
            where: { id },
            relations: { payer: true, payee: true },
        });
        if (!settlement)
            throw new common_1.NotFoundException('Settlement not found');
        if (settlement.payeeId !== userId) {
            throw new common_1.ForbiddenException('Only the payee can confirm a settlement');
        }
        if (settlement.isConfirmed) {
            throw new common_1.ConflictException('Settlement is already confirmed');
        }
        await this.settlementRepo.update({ id }, { isConfirmed: true });
        const updated = await this.settlementRepo.findOne({
            where: { id },
            relations: { payer: true, payee: true },
        });
        if (!updated)
            throw new common_1.NotFoundException('Settlement not found');
        return this.toDto(updated);
    }
    async assertGroupMember(groupId, userId) {
        const member = await this.groupMemberRepo.findOne({
            where: { groupId, userId, leftAt: (0, typeorm_2.IsNull)() },
        });
        if (!member) {
            throw new common_1.BadRequestException(`User ${userId} is not a member of this group`);
        }
    }
    async paginate(where, page, limit, sortOrder = 'desc') {
        const skip = (page - 1) * limit;
        const [settlements, total] = await Promise.all([
            this.settlementRepo.find({
                where,
                relations: { payer: true, payee: true },
                order: { createdAt: sortOrder.toUpperCase() },
                skip,
                take: limit,
            }),
            this.settlementRepo.count({ where }),
        ]);
        return this.buildPaginatedResult(settlements, total, page, limit);
    }
    async paginateQuery(qb, page, limit, sortOrder = 'desc') {
        const skip = (page - 1) * limit;
        qb.leftJoinAndSelect('settlement.payer', 'payer')
            .leftJoinAndSelect('settlement.payee', 'payee')
            .orderBy('settlement.createdAt', sortOrder.toUpperCase())
            .skip(skip)
            .take(limit);
        const [settlements, total] = await Promise.all([
            qb.getMany(),
            qb.clone().skip(undefined).take(undefined).getCount(),
        ]);
        return this.buildPaginatedResult(settlements, total, page, limit);
    }
    buildPaginatedResult(settlements, total, page, limit) {
        const totalPages = Math.ceil(total / limit);
        return {
            data: settlements.map((s) => this.toDto(s)),
            total,
            page,
            limit,
            totalPages,
            hasNextPage: page < totalPages,
        };
    }
    toDto(settlement) {
        return {
            id: settlement.id,
            payer: {
                id: settlement.payer.id,
                name: settlement.payer.name,
                avatarUrl: settlement.payer.avatarUrl,
            },
            payee: {
                id: settlement.payee.id,
                name: settlement.payee.name,
                avatarUrl: settlement.payee.avatarUrl,
            },
            amount: Number(settlement.amount),
            currency: settlement.currency,
            method: settlement.method,
            notes: settlement.notes,
            groupId: settlement.groupId,
            isConfirmed: settlement.isConfirmed,
            settledAt: settlement.settledAt,
            createdAt: settlement.createdAt,
        };
    }
};
exports.SettlementsService = SettlementsService;
exports.SettlementsService = SettlementsService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(settlement_entity_1.Settlement)),
    __param(1, (0, typeorm_1.InjectRepository)(user_entity_1.User)),
    __param(2, (0, typeorm_1.InjectRepository)(group_member_entity_1.GroupMember)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        activity_service_1.ActivityService,
        notifications_service_1.NotificationsService])
], SettlementsService);
//# sourceMappingURL=settlements.service.js.map