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
    ) { }

    async create(createOpportunityDto: CreateOpportunityDto) {
        try {
            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,
                    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,
                },
                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,
            },
            orderBy: { updatedAt: 'desc' }
        });
    }

    async findOne(id: string) {
        const opp = await this.prisma.opportunity.findUnique({
            where: { id },
            include: {
                client: true,
                user: 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 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);

            // 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 },
        });
    }
}
