import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { UsersService } from '../users/users.service';
import { user_role } from '@prisma/client';
import { startOfDay, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter, subMonths } from 'date-fns';

@Injectable()
export class DashboardService {
    constructor(
        private readonly prisma: PrismaService,
        private readonly usersService: UsersService
    ) { }

    async getStats(user: any) {
        let allowedUserIds: string[] = [user.id];

        if (user.role === user_role.ADMIN) {
            // Admin sees everything, leave allowedUserIds empty or filter differently
            // But for simplicity in query, we can use empty where if Admin
        } else {
            const subordinates = await this.usersService.getSubordinateIds(user.id);
            allowedUserIds = [user.id, ...subordinates];
        }

        const userFilter = user.role === user_role.ADMIN ? {} : { userId: { in: allowedUserIds } };
        const leadSharingFilter = user.role === user_role.ADMIN ? {} : { OR: [{ assignedTo: { in: allowedUserIds } }, { creatorId: { in: allowedUserIds } }] };
        const clientFilter = user.role === user_role.ADMIN ? {} : { assignedTo: { in: allowedUserIds } };

        const today = startOfDay(new Date());
        const monthStart = startOfMonth(new Date());

        const [
            enquiriesCount,
            enquiriesToday,
            opportunityStats,
            monthlyRevenue,
            clientsCount,
            recentEnquiries,
            recentOpportunities,
            pendingExpenses,
            overdueFollowups,
            todayFollowups,
            newItems
        ] = await Promise.all([
            // Total Enquiries
            this.prisma.enquiry.count({ where: userFilter }),

            // Enquiries Today
            this.prisma.enquiry.count({
                where: {
                    ...userFilter,
                    createdAt: { gte: today }
                }
            }),

            // Pipeline Stats (Non-WON)
            this.prisma.opportunity.aggregate({
                where: {
                    ...leadSharingFilter,
                    stage: { not: 'SALES' }
                },
                _sum: { value: true },
                _count: true
            }),

            // Monthly Revenue (WON this month)
            this.prisma.opportunity.aggregate({
                where: {
                    ...leadSharingFilter,
                    stage: 'SALES',
                    updatedAt: { gte: monthStart }
                },
                _sum: { value: true }
            }),

            // Total Customers (for conversion rate)
            this.prisma.client.count({
                where: {
                    ...clientFilter,
                    status: { in: ['SALES', 'CLOSED'] }
                }
            }),

            // Recent Activity - Enquiries
            this.prisma.enquiry.findMany({
                where: userFilter,
                take: 5,
                orderBy: { createdAt: 'desc' },
                include: { client: true, user: true }
            }),

            // Recent Activity - Opportunities
            this.prisma.opportunity.findMany({
                where: leadSharingFilter,
                take: 5,
                orderBy: { updatedAt: 'desc' },
                include: { client: true, user: true }
            }),

            // Pending Expenses (Managers/Admins)
            this.prisma.expense.count({
                where: {
                    ...userFilter,
                    status: 'PENDING'
                }
            }),

            // Overdue Followups
            this.prisma.followup.count({
                where: {
                    ...userFilter,
                    status: 'PENDING',
                    date: { lt: today }
                }
            }),

            // Today's Followups
            this.prisma.followup.count({
                where: {
                    ...userFilter,
                    status: 'PENDING',
                    date: { gte: today, lt: new Date(today.getTime() + 86400000) }
                }
            }),

            // New Leads/Opportunities (Last 24h)
            this.prisma.opportunity.count({
                where: {
                    ...leadSharingFilter,
                    createdAt: { gte: new Date(Date.now() - 24 * 60 * 60 * 1000) }
                }
            })
        ]);

        // Fetch Latest Performance Score
        const latestScore = await this.prisma.performanceScore.findFirst({
            where: { userId: user.id },
            orderBy: { date: 'desc' }
        });

        // Fetch Current Target
        const target = await this.prisma.target.findFirst({
            where: { userId: user.id, month: new Date().getMonth() + 1, year: new Date().getFullYear() }
        });

        // Calculate Contribution Revenue (50/50 Split)
        const wonOpportunities = await this.prisma.opportunity.findMany({
            where: {
                ...leadSharingFilter,
                stage: 'SALES',
                updatedAt: { gte: monthStart }
            },
            select: { value: true, assignedTo: true, creatorId: true }
        });

        const contributionRevenue = wonOpportunities.reduce((total, o) => {
            let share = 0;
            const isCloser = o.assignedTo === user.id;
            const isCreator = o.creatorId === user.id;
            if (isCloser && isCreator) share = o.value;
            else if (isCloser || isCreator) share = o.value * 0.5;
            return total + share;
        }, 0);

        // Calculate Conversion Rate
        const conversionRate = enquiriesCount > 0
            ? Math.round((clientsCount / enquiriesCount) * 100)
            : 0;

        return {
            kpis: {
                enquiriesToday,
                pipelineValue: opportunityStats._sum?.value || 0,
                pipelineCount: opportunityStats._count || 0,
                monthlyRevenue: monthlyRevenue._sum?.value || 0,
                contributionRevenue,
                conversionRate,
                pendingExpenses: (user.role === user_role.ADMIN || [user_role.GENERAL_MANAGER, user_role.MANAGER, user_role.OFFICER].includes(user.role)) ? pendingExpenses : 0,
                overdueCount: overdueFollowups,
                todayCount: todayFollowups,
                newCount: newItems
            },
            performance: latestScore ? {
                score: latestScore.score,
                tag: latestScore.tag,
                breakdown: {
                    revenue: latestScore.revenueScore,
                    conversion: latestScore.conversionScore,
                    activity: latestScore.activityScore,
                    discipline: latestScore.disciplineScore,
                    quality: latestScore.dataQualityScore
                }
            } : null,
            target: target ? {
                monthly: target.monthlyTarget,
                minimum: target.minTarget,
                weekly: target.weeklyTarget,
                dailyLead: target.dailyLeadTarget,
                achieved: monthlyRevenue._sum?.value || 0
            } : null,
            recentActivity: {
                enquiries: recentEnquiries,
                opportunities: recentOpportunities
            }
        };
    }

    // ─────────────────────────────────────────────
    // MY DASHBOARD  (salesperson / normal user)
    // ─────────────────────────────────────────────
    async getMyDashboard(user: any) {
        const now = new Date();
        const monthStart = startOfMonth(now);
        const monthEnd = endOfMonth(now);
        const userId = user.id;

        // Week buckets (fixed date ranges within the month)
        const year = now.getFullYear();
        const month = now.getMonth();
        const weeks = [
            { label: 'Week 1', start: new Date(year, month, 1), end: new Date(year, month, 7, 23, 59, 59) },
            { label: 'Week 2', start: new Date(year, month, 8), end: new Date(year, month, 14, 23, 59, 59) },
            { label: 'Week 3', start: new Date(year, month, 15), end: new Date(year, month, 21, 23, 59, 59) },
            { label: 'Week 4', start: new Date(year, month, 22), end: monthEnd },
        ];

        // Fetch target for current month
        const target = await this.prisma.target.findFirst({
            where: { userId, month: now.getMonth() + 1, year: now.getFullYear() }
        });

        // Fetch all WON opportunities this month where user is involved
        const wonOpps = await this.prisma.opportunity.findMany({
            where: {
                OR: [
                    { assignedTo: userId },
                    { creatorId: userId },
                    { closingOwnerId: userId },
                ],
                stage: 'SALES',
                updatedAt: { gte: monthStart, lte: monthEnd }
            },
            select: {
                value: true,
                assignedTo: true,
                creatorId: true,
                closingOwnerId: true,
                updatedAt: true,
            }
        });

        // Fetch all pipeline opportunities (non-won) this month
        const pipelineOpps = await this.prisma.opportunity.findMany({
            where: {
                OR: [{ assignedTo: userId }, { creatorId: userId }],
                stage: { not: 'SALES' }
            },
            select: { value: true, stage: true, assignedTo: true, creatorId: true }
        });

        // Weekly actual calculation (user's contribution per week)
        const weeklyActual = weeks.map(w => {
            const oppsInWeek = wonOpps.filter(o => o.updatedAt >= w.start && o.updatedAt <= w.end);
            return oppsInWeek.reduce((sum, o) => {
                const isCloser = (o.closingOwnerId || o.assignedTo) === userId;
                const isCreator = o.creatorId === userId;
                if (isCloser && isCreator) return sum + o.value;
                if (isCloser || isCreator) return sum + o.value * 0.5;
                return sum;
            }, 0);
        });

        const weeklyExpected = target ? weeks.map(() => target.weeklyTarget) : [0, 0, 0, 0];

        // Total MTD achieved (user contribution)
        const totalActual = wonOpps.reduce((sum, o) => {
            const isCloser = (o.closingOwnerId || o.assignedTo) === userId;
            const isCreator = o.creatorId === userId;
            if (isCloser && isCreator) return sum + o.value;
            if (isCloser || isCreator) return sum + o.value * 0.5;
            return sum;
        }, 0);

        const totalExpected = target?.monthlyTarget || 0;
        const achievementPct = totalExpected > 0 ? Math.min(100, (totalActual / totalExpected) * 100) : 0;
        const remaining = Math.max(0, totalExpected - totalActual);
        const remainingPct = totalExpected > 0 ? Math.max(0, 100 - achievementPct) : 0;

        // Stage-wise pipeline (count + value)
        const stages = ['LEAD', 'QUALIFIED', 'POTENTIAL', 'DEMO', 'SALES'];
        const stageData = stages.map(stage => {
            const oppsInStage = stage === 'SALES'
                ? wonOpps
                : pipelineOpps.filter(o => o.stage === stage);
            return {
                stage,
                count: oppsInStage.length,
                value: oppsInStage.reduce((s, o) => s + (o.value || 0), 0)
            };
        });

        // Own sales = user is both creator AND closer
        const ownSalesOpps = wonOpps.filter(o => {
            const closer = o.closingOwnerId || o.assignedTo;
            return closer === userId && o.creatorId === userId;
        });
        const ownSalesValue = ownSalesOpps.reduce((s, o) => s + o.value, 0);

        // Shared sales = user is either creator OR closer, but not both
        const sharedSalesOpps = wonOpps.filter(o => {
            const closer = o.closingOwnerId || o.assignedTo;
            const isCloser = closer === userId;
            const isCreator = o.creatorId === userId;
            return (isCloser || isCreator) && !(isCloser && isCreator);
        });
        // Contribution from shared (50% each)
        const sharedSalesValue = sharedSalesOpps.reduce((s, o) => s + o.value * 0.5, 0);

        const totalSalesValue = ownSalesValue + sharedSalesValue;
        const ownSalesPct = totalSalesValue > 0 ? (ownSalesValue / totalSalesValue) * 100 : 0;
        const sharedSalesPct = totalSalesValue > 0 ? (sharedSalesValue / totalSalesValue) * 100 : 0;

        return {
            target: target ? {
                monthly: target.monthlyTarget,
                minimum: target.minTarget,
                weekly: target.weeklyTarget,
                dailyLead: target.dailyLeadTarget,
                requiredClosures: (target as any).requiredClosures || null,
                requiredDemos: (target as any).requiredDemos || null,
            } : null,
            weekly: weeks.map((w, i) => ({
                label: w.label,
                expected: weeklyExpected[i],
                actual: weeklyActual[i],
            })),
            totalExpected,
            totalActual,
            achievementPct: Math.round(achievementPct * 10) / 10,
            remaining,
            remainingPct: Math.round(remainingPct * 10) / 10,
            stageData,
            ownSales: {
                value: ownSalesValue,
                count: ownSalesOpps.length,
                pct: Math.round(ownSalesPct * 10) / 10,
            },
            sharedSales: {
                value: sharedSalesValue,
                count: sharedSalesOpps.length,
                pct: Math.round(sharedSalesPct * 10) / 10,
            },
        };
    }

    // ─────────────────────────────────────────────
    // MANAGER DASHBOARD
    // ─────────────────────────────────────────────
    async getManagerDashboard(user: any) {
        const now = new Date();
        const monthStart = startOfMonth(now);
        const monthEnd = endOfMonth(now);
        const lastMonthStart = startOfMonth(subMonths(now, 1));
        const lastMonthEnd = endOfMonth(subMonths(now, 1));
        const quarterStart = startOfQuarter(now);
        const quarterEnd = endOfQuarter(now);

        // Determine visible team members
        let teamUserIds: string[] = [];
        if (user.role === user_role.ADMIN || user.role === user_role.GENERAL_MANAGER) {
            const allUsers = await this.prisma.user.findMany({
                where: { status: 'APPROVED' },
                select: { id: true }
            });
            teamUserIds = allUsers.map(u => u.id);
        } else {
            const subs = await this.usersService.getSubordinateIds(user.id);
            teamUserIds = [user.id, ...subs];
        }

        // ── Team Performance ──
        const teamUsers = await this.prisma.user.findMany({
            where: { id: { in: teamUserIds }, status: 'APPROVED' },
            select: {
                id: true,
                name: true,
                role: true,
                performanceScores: {
                    orderBy: { date: 'desc' },
                    take: 1,
                }
            }
        });

        const teamPerformance = teamUsers.map(u => ({
            id: u.id,
            name: u.name,
            role: u.role,
            score: u.performanceScores[0]?.score || 0,
            tag: u.performanceScores[0]?.tag || 'N/A',
            revenueScore: (u.performanceScores[0] as any)?.revenueScore || 0,
            activityScore: (u.performanceScores[0] as any)?.activityScore || 0,
        }));

        // ── Pipeline Health (stage-wise aggregate for team) ──
        const allOpps = await this.prisma.opportunity.findMany({
            where: {
                OR: [{ assignedTo: { in: teamUserIds } }, { creatorId: { in: teamUserIds } }]
            },
            select: { stage: true, value: true, assignedTo: true, creatorId: true, updatedAt: true, closingOwnerId: true }
        });

        const stages = ['LEAD', 'QUALIFIED', 'POTENTIAL', 'DEMO', 'SALES'];
        const pipelineHealth = stages.map(stage => {
            const inStage = allOpps.filter(o => o.stage === stage);
            return {
                stage,
                count: inStage.length,
                value: inStage.reduce((s, o) => s + (o.value || 0), 0)
            };
        });

        // ── Target vs Sales Conversion Ratio ──
        const computeConversionRatio = async (start: Date, end: Date) => {
            const [totalTarget, wonRevenue, totalEnquiries] = await Promise.all([
                this.prisma.target.aggregate({
                    where: { userId: { in: teamUserIds }, month: start.getMonth() + 1, year: start.getFullYear() },
                    _sum: { monthlyTarget: true }
                }),
                this.prisma.opportunity.aggregate({
                    where: {
                        assignedTo: { in: teamUserIds },
                        stage: 'SALES',
                        updatedAt: { gte: start, lte: end }
                    },
                    _sum: { value: true },
                    _count: true
                }),
                this.prisma.enquiry.count({
                    where: {
                        userId: { in: teamUserIds },
                        createdAt: { gte: start, lte: end }
                    }
                })
            ]);

            const target = totalTarget._sum?.monthlyTarget || 0;
            const revenue = wonRevenue._sum?.value || 0;
            const closures = wonRevenue._count || 0;
            const ratio = target > 0 ? Math.round((revenue / target) * 100) : 0;
            const conversionRate = totalEnquiries > 0 ? Math.round((closures / totalEnquiries) * 100) : 0;
            return { target, revenue, closures, ratio, enquiries: totalEnquiries, conversionRate };
        };

        const [thisMonth, lastMonth, quarter] = await Promise.all([
            computeConversionRatio(monthStart, monthEnd),
            computeConversionRatio(lastMonthStart, lastMonthEnd),
            computeConversionRatio(quarterStart, quarterEnd),
        ]);

        // ── Activities Analysis ──
        const activityTypes = ['DEMO', 'VISIT', 'MANAGER_HELP', 'CALL', 'MEETING'];
        const scheduledFollowups = await this.prisma.followup.findMany({
            where: {
                userId: { in: teamUserIds },
                date: { gte: monthStart, lte: monthEnd }
            },
            select: { type: true, status: true }
        });

        const activitiesAnalysis = activityTypes.map(type => {
            const typeFollowups = scheduledFollowups.filter(f => (f as any).type === type);
            const scheduled = typeFollowups.length;
            const completed = typeFollowups.filter(f => f.status === 'DONE').length;
            return { type, scheduled, completed, completionRate: scheduled > 0 ? Math.round((completed / scheduled) * 100) : 0 };
        });

        // Also get strategic activities (demos, visits)
        const strategicActivities = await this.prisma.strategicActivity.findMany({
            where: {
                userId: { in: teamUserIds },
                date: { gte: monthStart, lte: monthEnd } as any
            },
            select: { type: true }
        });

        const strategicByType: Record<string, number> = {};
        strategicActivities.forEach(a => {
            strategicByType[a.type] = (strategicByType[a.type] || 0) + 1;
        });

        // ── Performance Weightage ──
        // 70% Sales Performance + 30% Activity Performance per user
        const performanceWeightage = teamUsers.map(u => {
            const ps = u.performanceScores[0];
            const salesScore = ps ? ((ps as any).revenueScore || 0) + ((ps as any).conversionScore || 0) : 0;
            const activityScore = ps ? ((ps as any).activityScore || 0) + ((ps as any).disciplineScore || 0) : 0;
            // Sales max = 60 (40 revenue + 20 conversion), Activity max = 30 (15 + 15)
            const weightedSales = Math.min(70, (salesScore / 60) * 70);
            const weightedActivity = Math.min(30, (activityScore / 30) * 30);
            return {
                id: u.id,
                name: u.name,
                weightedSales: Math.round(weightedSales * 10) / 10,
                weightedActivity: Math.round(weightedActivity * 10) / 10,
                totalWeighted: Math.round((weightedSales + weightedActivity) * 10) / 10,
            };
        });

        // ── Performer / Underperformer (Month) ──
        const sorted = [...teamPerformance].sort((a, b) => b.score - a.score);
        const topPerformers = sorted.slice(0, 3);
        const underPerformers = sorted.slice(-3).reverse();

        // ── Performer / Underperformer (Quarter) ──
        const quarterScores = await this.prisma.performanceScore.findMany({
            where: {
                userId: { in: teamUserIds },
                date: { gte: quarterStart, lte: quarterEnd }
            },
            select: { userId: true, score: true }
        });

        const quarterAvgByUser: Record<string, { total: number; count: number }> = {};
        quarterScores.forEach(s => {
            if (!quarterAvgByUser[s.userId]) quarterAvgByUser[s.userId] = { total: 0, count: 0 };
            quarterAvgByUser[s.userId].total += s.score;
            quarterAvgByUser[s.userId].count += 1;
        });

        const quarterUserScores = teamUsers.map(u => ({
            id: u.id,
            name: u.name,
            role: u.role,
            score: quarterAvgByUser[u.id]
                ? Math.round(quarterAvgByUser[u.id].total / quarterAvgByUser[u.id].count)
                : 0,
        })).sort((a, b) => b.score - a.score);

        const topPerformersQuarter = quarterUserScores.slice(0, 3);
        const underPerformersQuarter = quarterUserScores.slice(-3).reverse();

        // ── Lead vs Shared Conversion ──
        const thisMonthWonOpps = allOpps.filter(o =>
            o.stage === 'SALES' &&
            o.updatedAt >= monthStart &&
            o.updatedAt <= monthEnd
        );

        const ownBusinessOpps = thisMonthWonOpps.filter(o => {
            const closer = o.closingOwnerId || o.assignedTo;
            return closer === o.creatorId;
        });

        const sharedBusinessOpps = thisMonthWonOpps.filter(o => {
            const closer = o.closingOwnerId || o.assignedTo;
            return closer !== o.creatorId;
        });

        const ownBusinessValue = ownBusinessOpps.reduce((s, o) => s + o.value, 0);
        const sharedBusinessValue = sharedBusinessOpps.reduce((s, o) => s + o.value, 0);

        // Enquiries for conversion calc
        const totalEnquiriesMonth = await this.prisma.enquiry.count({
            where: { userId: { in: teamUserIds }, createdAt: { gte: monthStart, lte: monthEnd } }
        });

        const ownConvRate = totalEnquiriesMonth > 0
            ? Math.round((ownBusinessOpps.length / totalEnquiriesMonth) * 100) : 0;
        const sharedConvRate = totalEnquiriesMonth > 0
            ? Math.round((sharedBusinessOpps.length / totalEnquiriesMonth) * 100) : 0;

        // ── Revenue Contribution Tracker ──
        const revenueContribution = thisMonthWonOpps.map(o => {
            const leadOwner = teamUsers.find(u => u.id === o.creatorId)?.name || 'Unknown';
            const closingOwner = teamUsers.find(u => u.id === (o.closingOwnerId || o.assignedTo))?.name || 'Unknown';
            const isOwn = (o.closingOwnerId || o.assignedTo) === o.creatorId;
            return {
                value: o.value,
                leadOwner,
                closingOwner,
                isOwn,
                leadOwnerShare: isOwn ? o.value : o.value * 0.5,
                closingOwnerShare: isOwn ? o.value : o.value * 0.5,
            };
        }).slice(0, 20); // Cap at 20 for display

        return {
            teamPerformance,
            pipelineHealth,
            conversionRatio: { thisMonth, lastMonth, quarter },
            activitiesAnalysis,
            strategicByType,
            performanceWeightage,
            performers: {
                month: { top: topPerformers, under: underPerformers },
                quarter: { top: topPerformersQuarter, under: underPerformersQuarter },
            },
            leadVsShared: {
                own: {
                    count: ownBusinessOpps.length,
                    value: ownBusinessValue,
                    conversionRate: ownConvRate,
                },
                shared: {
                    count: sharedBusinessOpps.length,
                    value: sharedBusinessValue,
                    conversionRate: sharedConvRate,
                },
                totalEnquiries: totalEnquiriesMonth,
            },
            revenueContribution,
        };
    }
}
