"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.ReportsService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const expense_entity_1 = require("../../database/entities/expense.entity");
const settlement_entity_1 = require("../../database/entities/settlement.entity");
const group_entity_1 = require("../../database/entities/group.entity");
const group_member_entity_1 = require("../../database/entities/group-member.entity");
let ReportsService = class ReportsService {
    constructor(expenseRepo, settlementRepo, groupRepo, groupMemberRepo) {
        this.expenseRepo = expenseRepo;
        this.settlementRepo = settlementRepo;
        this.groupRepo = groupRepo;
        this.groupMemberRepo = groupMemberRepo;
    }
    async getMonthlyReport(userId, query) {
        const months = query.months ?? 12;
        const from = new Date();
        from.setMonth(from.getMonth() - months + 1);
        from.setDate(1);
        from.setHours(0, 0, 0, 0);
        const expenseQb = this.expenseRepo
            .createQueryBuilder('expense')
            .innerJoin('expense.splits', 'split', 'split.userId = :userId', { userId })
            .leftJoinAndSelect('expense.splits', 'splits', 'splits.userId = :userId', { userId })
            .where('expense.isDeleted = false')
            .andWhere('expense.date >= :from', { from });
        if (query.groupId) {
            expenseQb.andWhere('expense.groupId = :groupId', { groupId: query.groupId });
        }
        const expenses = await expenseQb.orderBy('expense.date', 'ASC').getMany();
        const settlementWhere = {
            isConfirmed: true,
            payerId: userId,
            createdAt: (0, typeorm_2.MoreThanOrEqual)(from),
        };
        if (query.groupId) {
            settlementWhere.groupId = query.groupId;
        }
        const settlements = await this.settlementRepo.find({
            where: settlementWhere,
            select: { amount: true, createdAt: true },
        });
        const buckets = this.buildMonthBuckets(months);
        for (const expense of expenses) {
            const key = this.toMonthKey(expense.date);
            if (buckets[key]) {
                const myShare = (expense.splits ?? []).reduce((sum, s) => sum + Number(s.amount), 0);
                const iPaid = expense.paidById === userId;
                buckets[key].expenseCount += 1;
                buckets[key].totalOwed += myShare;
                if (iPaid) {
                    buckets[key].totalPaid += Number(expense.amount);
                }
            }
        }
        for (const settlement of settlements) {
            const key = this.toMonthKey(settlement.createdAt);
            if (buckets[key]) {
                buckets[key].totalSettled += Number(settlement.amount);
            }
        }
        return Object.values(buckets).map((b) => ({
            month: b.month,
            totalPaid: this.round(b.totalPaid),
            totalOwed: this.round(b.totalOwed),
            totalSettled: this.round(b.totalSettled),
            expenseCount: b.expenseCount,
        }));
    }
    async getCategoryBreakdown(userId, query) {
        const months = query.months ?? 12;
        const from = new Date();
        from.setMonth(from.getMonth() - months);
        const qb = this.expenseRepo
            .createQueryBuilder('expense')
            .innerJoin('expense.splits', 'split', 'split.userId = :userId', { userId })
            .select(['expense.category', 'expense.amount'])
            .where('expense.isDeleted = false')
            .andWhere('expense.date >= :from', { from });
        if (query.groupId) {
            qb.andWhere('expense.groupId = :groupId', { groupId: query.groupId });
        }
        const expenses = await qb.getMany();
        const categoryMap = {};
        for (const e of expenses) {
            const cat = e.category;
            if (!categoryMap[cat])
                categoryMap[cat] = { total: 0, count: 0 };
            categoryMap[cat].total += Number(e.amount);
            categoryMap[cat].count += 1;
        }
        const grandTotal = Object.values(categoryMap).reduce((sum, v) => sum + v.total, 0);
        return Object.entries(categoryMap)
            .map(([category, { total, count }]) => ({
            category,
            totalAmount: this.round(total),
            percentage: grandTotal > 0 ? this.round((total / grandTotal) * 100) : 0,
            expenseCount: count,
        }))
            .sort((a, b) => b.totalAmount - a.totalAmount);
    }
    async getGroupReport(groupId, userId) {
        const group = await this.groupRepo.findOne({
            where: { id: groupId, isActive: true },
        });
        if (!group)
            throw new common_1.NotFoundException('Group not found');
        const member = await this.groupMemberRepo.findOne({
            where: { groupId, userId, leftAt: (0, typeorm_2.IsNull)() },
        });
        if (!member)
            throw new common_1.ForbiddenException('You are not a member of this group');
        const [expenses, settledTotal, categoryMap, monthlyData, memberCount] = await Promise.all([
            this.expenseRepo.find({
                where: { groupId, isDeleted: false },
                select: { amount: true, category: true, date: true },
            }),
            this.sumConfirmedSettlements(groupId),
            this.buildCategoryMap(groupId),
            this.buildMonthlyTrend(groupId),
            this.groupMemberRepo.count({ where: { groupId, leftAt: (0, typeorm_2.IsNull)() } }),
        ]);
        const totalAmount = expenses.reduce((sum, e) => sum + Number(e.amount), 0);
        const grandTotal = totalAmount;
        const categoryBreakdown = Object.entries(categoryMap)
            .map(([category, { total, count }]) => ({
            category,
            totalAmount: this.round(total),
            percentage: grandTotal > 0 ? this.round((total / grandTotal) * 100) : 0,
            expenseCount: count,
        }))
            .sort((a, b) => b.totalAmount - a.totalAmount);
        return {
            groupId: group.id,
            groupName: group.name,
            currency: group.currency,
            totalExpenses: expenses.length,
            totalAmount: this.round(totalAmount),
            totalSettled: this.round(settledTotal),
            memberCount,
            categoryBreakdown,
            monthlyTrend: monthlyData,
        };
    }
    async sumConfirmedSettlements(groupId) {
        const result = await this.settlementRepo
            .createQueryBuilder('s')
            .select('COALESCE(SUM(s.amount), 0)', 'total')
            .where('s.groupId = :groupId', { groupId })
            .andWhere('s.isConfirmed = true')
            .getRawOne();
        return Number(result?.total ?? 0);
    }
    async buildCategoryMap(groupId) {
        const expenses = await this.expenseRepo.find({
            where: { groupId, isDeleted: false },
            select: { category: true, amount: true },
        });
        const map = {};
        for (const e of expenses) {
            const cat = e.category;
            if (!map[cat])
                map[cat] = { total: 0, count: 0 };
            map[cat].total += Number(e.amount);
            map[cat].count += 1;
        }
        return map;
    }
    async buildMonthlyTrend(groupId) {
        const from = new Date();
        from.setMonth(from.getMonth() - 11);
        from.setDate(1);
        const [expenses, settlements] = await Promise.all([
            this.expenseRepo.find({
                where: { groupId, isDeleted: false, date: (0, typeorm_2.MoreThanOrEqual)(from) },
                select: { amount: true, date: true },
            }),
            this.settlementRepo.find({
                where: { groupId, isConfirmed: true, createdAt: (0, typeorm_2.MoreThanOrEqual)(from) },
                select: { amount: true, createdAt: true },
            }),
        ]);
        const buckets = this.buildMonthBuckets(12);
        for (const e of expenses) {
            const key = this.toMonthKey(e.date);
            if (buckets[key]) {
                buckets[key].totalPaid += Number(e.amount);
                buckets[key].expenseCount += 1;
            }
        }
        for (const s of settlements) {
            const key = this.toMonthKey(s.createdAt);
            if (buckets[key])
                buckets[key].totalSettled += Number(s.amount);
        }
        return Object.values(buckets).map((b) => ({
            month: b.month,
            totalPaid: this.round(b.totalPaid),
            totalOwed: 0,
            totalSettled: this.round(b.totalSettled),
            expenseCount: b.expenseCount,
        }));
    }
    buildMonthBuckets(months) {
        const buckets = {};
        const now = new Date();
        for (let i = months - 1; i >= 0; i--) {
            const d = new Date(now.getFullYear(), now.getMonth() - i, 1);
            const key = this.toMonthKey(d);
            buckets[key] = { month: key, totalPaid: 0, totalOwed: 0, totalSettled: 0, expenseCount: 0 };
        }
        return buckets;
    }
    toMonthKey(date) {
        return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
    }
    round(value) {
        return Math.round(value * 100) / 100;
    }
};
exports.ReportsService = ReportsService;
exports.ReportsService = ReportsService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, typeorm_1.InjectRepository)(expense_entity_1.Expense)),
    __param(1, (0, typeorm_1.InjectRepository)(settlement_entity_1.Settlement)),
    __param(2, (0, typeorm_1.InjectRepository)(group_entity_1.Group)),
    __param(3, (0, typeorm_1.InjectRepository)(group_member_entity_1.GroupMember)),
    __metadata("design:paramtypes", [typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository,
        typeorm_2.Repository])
], ReportsService);
//# sourceMappingURL=reports.service.js.map