import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreateOpportunityDto } from './dto/create-opportunity.dto';
import { UpdateOpportunityDto } from './dto/update-opportunity.dto';
import { UsersService } from '../users/users.service';
import { WhatsappService } from '../whatsapp/whatsapp.service';
import { user_role } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';

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

    private async validateDiscount(
        title: string,
        assignedTo: string,
        discountValue: number,
        selectedSchemeId?: string | null
    ) {
        if (!discountValue || discountValue <= 0) return;

        // 1. Find product by name
        const product = await this.prisma.product.findFirst({
            where: { name: title },
            include: { schemes: true }
        });

        if (!product) return;

        // 2. Determine base price and scheme ID
        let basePrice = product.price;
        let schemeId: string | null = null;
        let scheme: any = null;

        if (selectedSchemeId && selectedSchemeId !== 'base') {
            scheme = product.schemes.find(s => s.id === selectedSchemeId);
            if (scheme) {
                basePrice = scheme.price;
                schemeId = scheme.id;
            }
        }

        if (basePrice <= 0) return;

        // 3. Find applicable cap
        let allowedPercentage: number | null = null;

        // A. User-Scheme cap
        if (schemeId && assignedTo) {
            const userSchemeCap = await this.prisma.userProductDiscount.findFirst({
                where: { userId: assignedTo, productId: product.id, schemeId }
            });
            if (userSchemeCap) {
                allowedPercentage = userSchemeCap.maxDiscountPercentage;
            }
        }

        // B. User-Product cap
        if (allowedPercentage === null && assignedTo) {
            const userProductCap = await this.prisma.userProductDiscount.findFirst({
                where: { userId: assignedTo, productId: product.id, schemeId: null }
            });
            if (userProductCap) {
                allowedPercentage = userProductCap.maxDiscountPercentage;
            }
        }

        // C. Scheme fallback cap
        if (allowedPercentage === null && scheme) {
            allowedPercentage = scheme.maxDiscountPercentage;
        }

        // D. Product fallback cap
        if (allowedPercentage === null) {
            allowedPercentage = product.maxDiscountPercentage;
        }

        // E. Ultimate default if everything is null
        if (allowedPercentage === null) {
            allowedPercentage = 100;
        }

        // 4. Calculate applied discount percentage
        const appliedPercentage = (discountValue / basePrice) * 100;

        // Check if limit exceeded (adding a small epsilon for floating point issues)
        if (appliedPercentage > allowedPercentage + 0.001) {
            const maxAllowedValue = (basePrice * allowedPercentage) / 100;
            throw new BadRequestException(
                `Applied discount of ₹${discountValue.toLocaleString()} (${appliedPercentage.toFixed(1)}%) exceeds the maximum allowed discount of ${allowedPercentage}% (₹${maxAllowedValue.toLocaleString()}) for this product.`
            );
        }
    }

    async create(createOpportunityDto: CreateOpportunityDto) {
        try {
            await this.validateDiscount(
                createOpportunityDto.title,
                createOpportunityDto.assignedTo,
                createOpportunityDto.discountValue ?? 0,
                createOpportunityDto.selectedSchemeId
            );

            return await this.prisma.opportunity.create({
                data: {
                    id: uuidv4(),
                    title: createOpportunityDto.title,
                    value: Number(createOpportunityDto.value),
                    clientId: createOpportunityDto.clientId,
                    assignedTo: createOpportunityDto.assignedTo,
                    stage: createOpportunityDto.stage || 'LEAD',
                    priority: createOpportunityDto.priority,
                    expectedCloseDate: (createOpportunityDto.expectedCloseDate && createOpportunityDto.expectedCloseDate.trim() !== '')
                        ? new Date(createOpportunityDto.expectedCloseDate)
                        : null,
                    creatorId: createOpportunityDto.creatorId,
                    closingProbability: createOpportunityDto.closingProbability ? Number(createOpportunityDto.closingProbability) : 0,
                    expectedClosingTimeframe: createOpportunityDto.expectedClosingTimeframe || null,
                    selectedSchemeId: createOpportunityDto.selectedSchemeId || null,
                    addonsSelected: createOpportunityDto.addonsSelected || null,
                    discountValue: createOpportunityDto.discountValue !== undefined ? Number(createOpportunityDto.discountValue) : 0,
                    isAmcMarked: !!createOpportunityDto.isAmcMarked,
                    amcPercentage: createOpportunityDto.amcPercentage !== undefined ? Number(createOpportunityDto.amcPercentage) : 0,
                    extraCharges: createOpportunityDto.extraCharges !== undefined ? Number(createOpportunityDto.extraCharges) : 0,
                    updatedAt: new Date(),
                },
            });
        } catch (error) {
            console.error('CREATE OPPORTUNITY ERROR:', error);
            throw error;
        }
    }

    async findAll(user: any) {
        // ADMIN and GENERAL_MANAGER see everything
        if (user.role === 'ADMIN' || user.role === 'GENERAL_MANAGER') {
            return this.prisma.opportunity.findMany({
                include: {
                    client: true,
                    user: true,
                    activities: true,
                },
                orderBy: { updatedAt: 'desc' }
            });
        }

        const subordinateIds = await this.usersService.getSubordinateIds(user.id);
        const allowedUserIds = [user.id, ...subordinateIds];

        return this.prisma.opportunity.findMany({
            where: {
                assignedTo: { in: allowedUserIds }
            },
            include: {
                client: true,
                user: true,
                activities: true,
            },
            orderBy: { updatedAt: 'desc' }
        });
    }

    async findOne(id: string) {
        const opp = await this.prisma.opportunity.findUnique({
            where: { id },
            include: {
                client: true,
                user: true,
                activities: true,
            },
        });
        if (!opp) throw new NotFoundException('Opportunity not found');
        return opp;
    }

    async update(id: string, updateOpportunityDto: UpdateOpportunityDto) {
        try {
            // Fetch current state to check if we are changing stage
            const current = await this.prisma.opportunity.findUnique({ where: { id } });
            if (!current) throw new NotFoundException('Opportunity not found');

            const title = updateOpportunityDto.title !== undefined ? updateOpportunityDto.title : current.title;
            const assignedTo = updateOpportunityDto.assignedTo !== undefined ? updateOpportunityDto.assignedTo : current.assignedTo;
            const discountValue = updateOpportunityDto.discountValue !== undefined ? Number(updateOpportunityDto.discountValue) : Number(current.discountValue || 0);
            const selectedSchemeId = updateOpportunityDto.selectedSchemeId !== undefined ? updateOpportunityDto.selectedSchemeId : current.selectedSchemeId;

            await this.validateDiscount(title, assignedTo, discountValue, selectedSchemeId);

            const newStage = updateOpportunityDto.stage || current.stage;

            // Validation Logic for Demo
            if (updateOpportunityDto.isDemoDone === true && !current.isDemoDone) {
                const missing: string[] = [];
                const check = (val: any) => val === null || val === undefined || (typeof val === 'string' && val.trim() === '');

                if (check(updateOpportunityDto.demoPersonName ?? current.demoPersonName)) missing.push('Person Name');
                if (check(updateOpportunityDto.demoContactDetails ?? current.demoContactDetails)) missing.push('Contact Details');
                if (check(updateOpportunityDto.expectedCloseDate ?? current.expectedCloseDate)) missing.push('Expected Close Date');
                if (check(updateOpportunityDto.value ?? current.value)) missing.push('Value');

                if (missing.length > 0) {
                    throw new BadRequestException(`Missing details to mark Demo as done: ${missing.join(', ')}`);
                }
            }

            if (newStage === 'SALES') {
                const missing: string[] = [];
                const check = (val: any) => val === null || val === undefined || (typeof val === 'string' && val.trim() === '');

                // Only Payment Mode is strictly mandatory for WON
                if (check(updateOpportunityDto.paymentMode ?? current.paymentMode)) missing.push('Payment Mode');

                if (missing.length > 0) {
                    throw new BadRequestException(`Missing for WON stage: ${missing.join(', ')}`);
                }
            }

            const data: any = { ...updateOpportunityDto };

            // Handle expectedCloseDate conversion
            if (updateOpportunityDto.expectedCloseDate && updateOpportunityDto.expectedCloseDate.trim() !== '') {
                data.expectedCloseDate = new Date(updateOpportunityDto.expectedCloseDate);
            } else if (updateOpportunityDto.expectedCloseDate === '') {
                data.expectedCloseDate = null;
            }

            if (data.value) data.value = Number(data.value);
            if (data.specialRate) data.specialRate = Number(data.specialRate);
            if (data.discountValue !== undefined) data.discountValue = Number(data.discountValue);
            if (data.amcPercentage !== undefined) data.amcPercentage = Number(data.amcPercentage);
            if (data.extraCharges !== undefined) data.extraCharges = Number(data.extraCharges);
            if (data.isAmcMarked !== undefined) data.isAmcMarked = !!data.isAmcMarked;

            // Fix foreign key constraint errors for empty strings
            if (data.creatorId === '') data.creatorId = null;
            if (data.demoOwnerId === '') data.demoOwnerId = null;
            if (data.closingOwnerId === '') data.closingOwnerId = null;

            return await this.prisma.opportunity.update({
                where: { id },
                data,
                include: { client: true }
            }).then(async (updated) => {
                if (current.stage !== updated.stage) {
                    try {
                        await this.whatsappService.sendPipelineUpdate(
                            updated.client.phone,
                            updated.client.name,
                            current.stage,
                            updated.stage
                        );
                    } catch (err) {
                        console.error('Failed to send pipeline WhatsApp:', err.message);
                    }
                }
                return updated;
            });
        } catch (error) {
            console.error('UPDATE OPPORTUNITY ERROR:', error);
            throw error;
        }
    }

    async remove(id: string) {
        return this.prisma.opportunity.delete({
            where: { id },
        });
    }
}
