import {
    HttpException,
    HttpStatus,
    Injectable,
    UnauthorizedException,
} from '@nestjs/common';
// import { ModelService } from 'src/model/model.service';

import { ModelService } from '../model/model.service';
import { CommonService } from '../common/common.service';
import moment from 'moment';
import * as path from "path";
import * as fs from "fs";
import * as dto from './dto/index';
import { Types } from 'mongoose';
import { agent_aggregation } from './aggregation/agent.aggregation';

@Injectable()
export class AgentService {
    constructor(
        private readonly Model: ModelService,
        private readonly CommonService: CommonService
    ) {

    }


    async create_agent(dto: dto.Agent.agentCreate, req: any) {
        try {
            const { _id: vendor_id } = req.user_data

            const find_workspace = await this.Model.UserWorkspaceModel.findOne({
                vendor_id: new Types.ObjectId(vendor_id),
            })
            if (!find_workspace) throw new HttpException("You ar Not in this workspace", HttpStatus.BAD_REQUEST);

            console.log('find_workspace.workspace_id', find_workspace.workspace_id)

            const subscription = await this.Model.subscriptionModel.findOne({
                workspace_id: find_workspace.workspace_id,
                is_active: true
            });
            if (!subscription) throw new HttpException(`You don't have any subscription plan! Please purchase any subscription.`, HttpStatus.BAD_REQUEST);

            if (subscription.agent_limit_left === 0) {
                throw new HttpException(`You have reached your AI Agent creation limits.`, HttpStatus.BAD_REQUEST);
            }

            const { phone_no, country_code, knowledge_base_id, name, image, call_prompt, call_first_message, chat_prompt, chat_first_message, type, voice, ai_model, ai_model_version, idle_reminder, call_cut_ability, call_transfer, send_message, real_time_booking, twilio_config } = dto

            if(twilio_config){
                const twilio_config_exist = await this.Model.AgentModel.findOne({ twilio_config: new Types.ObjectId(twilio_config) });
                if(twilio_config_exist) throw new HttpException(`This phone number already assigned! Please select another number.`, HttpStatus.BAD_REQUEST);
            }

            const result = await this.Model.AgentModel.create({
                workspace_id: new Types.ObjectId(find_workspace?.workspace_id),
                call_prompt: call_prompt,
                call_first_message: call_first_message,
                chat_prompt: chat_prompt,
                chat_first_message: chat_first_message,
                voice: voice,
                type: type,
                name: name,
                image: image,
                knowledge_base_id: knowledge_base_id,
                ai_model: ai_model,
                ai_model_version: ai_model_version,
                idle_reminder: idle_reminder,
                call_cut_ability: call_cut_ability,
                call_transfer: call_transfer,
                send_message: send_message,
                real_time_booking: real_time_booking,
                phone_no: phone_no,
                country_code: country_code,
                twilio_config: new Types.ObjectId(twilio_config)
            })

            await this.Model.subscriptionModel.findOneAndUpdate({ _id: subscription._id }, { agent_limit_left: (subscription.agent_limit_left - 1) }, { new: true })
            return result
        } catch (error) {
            throw error
        }
    }

    async edit_agent(agent_id: string, dto: dto.Agent.agentEdit) {
        try {
            const { name, image, call_prompt, call_first_message, chat_prompt, chat_first_message, knowledge_base_id, ai_model, voice, twilio_config } = dto

            const knowledge_base_ids = knowledge_base_id.map((ele) => {
                return new Types.ObjectId(ele)
            })

            if(twilio_config){
                const twilio_config_exist = await this.Model.AgentModel.findOne({ twilio_config: new Types.ObjectId(twilio_config), _id: { $ne: new Types.ObjectId(agent_id) } });
                if(twilio_config_exist) throw new HttpException(`This phone number already assigned! Please select another number.`, HttpStatus.BAD_REQUEST);
            }

            return await this.Model.AgentModel.findOneAndUpdate({ _id: new Types.ObjectId(agent_id) },
                {
                    ...(call_prompt && { call_prompt: call_prompt }),
                    ...(call_first_message && { call_first_message: call_first_message }),
                    ...(chat_prompt && { chat_prompt: chat_prompt }),
                    ...(chat_first_message && { chat_first_message: chat_first_message }),
                    ...(name && { name: name }),
                    ...(image && { image: image }),
                    ...(knowledge_base_id.length && { knowledge_base_id: knowledge_base_ids }),
                    ...(ai_model && { ai_model: ai_model }),
                    ...(voice && { voice: voice }),
                    ...(twilio_config && { twilio_config: new Types.ObjectId(twilio_config) }),
                },
                { lean: true, new: true })

        } catch (error) {
            throw error
        }
    }


    async get_agent(dto: dto.Agent.get_agent, req: any) {
        try {
            let { _id: vendor_id } = req.user_data

            let find_workspace = await this.Model.UserWorkspaceModel.findOne({
                vendor_id: new Types.ObjectId(vendor_id),
            })
            if (!find_workspace) throw new HttpException("You are Not in this workspace", HttpStatus.BAD_REQUEST);

            const temp_update = await this.Model.AgentModel.updateMany({ workspace_id: find_workspace?.workspace_id, is_deleted: { $ne: true } }, { is_deleted: false }, { new: true } )
            console.log('temp_update', temp_update)

            let { pagination, limit } = dto

            let query = [
                await agent_aggregation.match_data(find_workspace?.workspace_id),
                await agent_aggregation.lookup_chat(),
                await agent_aggregation.lookup_call(),
                await agent_aggregation.lookup_voice_call(),
                await agent_aggregation.add_fields(),
                await agent_aggregation.lookup_twilio_config(),
                await agent_aggregation.unwind_twilio_config(),
                await agent_aggregation.lookup_workspace_data(),
                await agent_aggregation.unwind_workspace_data(),
                await agent_aggregation.lookup_script_data(),
                await agent_aggregation.unwind_script_data(),
                await agent_aggregation.group_data(),
                await agent_aggregation.facetData(pagination, limit),
                await agent_aggregation.projectData(),
            ]
            let data = await this.Model.AgentModel.aggregate(query)
            return { count: data[0]?.count || 0, data: data[0]?.data || [] };
        } catch (error) {
            throw error
        }
    }


    async get_agent_detail(agent_id: string, req: any) {
        try {
            // let { _id: vendor_id } = req.user_data

            let data = await this.Model.AgentModel.findOne({ _id: new Types.ObjectId(agent_id) }, {}, { lean: true }).populate([
                { path: "knowledge_base_id" },
                { path: "twilio_config", select: "_id phone_number" },
            ])
            let count = await this.Model.CallModel.countDocuments({ agent_id: new Types.ObjectId(agent_id), type: 'CHAT' });
            const web_script = await this.Model.scriptModel.findOne({ agent_id: new Types.ObjectId(agent_id) })
            Object.assign(data, {
                chat_count: count,
                script_data: web_script,
            })
            return data
        } catch (error) {
            throw error
        }
    }

    async default_agent(dto: dto.Agent.defaultAgentCreate, req: any) {
        try {

            let { name, image, prompt, first_message, type, voice, ai_model, ai_model_version, idle_reminder, call_cut_ability, call_transfer, send_message, real_time_booking } = dto
            return await this.Model.defaultAgentsModel.create({
                prompt: prompt,
                first_message: first_message,
                name: name,
                image: image,
                type: type,
                voice: voice,
                ai_model: ai_model,
                ai_model_version: ai_model_version,
                idle_reminder: idle_reminder,
                call_cut_ability: call_cut_ability,
                call_transfer: call_transfer,
                send_message: send_message,
                real_time_booking: real_time_booking
            })
        } catch (error) {
            throw error
        }
    }



    async default_agent_get(dto: dto.Agent.get_agent, req: any) {
        try {

            let { pagination, limit } = dto
            let options = await this.CommonService.set_options(pagination, limit)
            let data = await this.Model.defaultAgentsModel.find({}, {}, options)
            console.log("data", data);

            let count = await this.Model.defaultAgentsModel.countDocuments()
            return { count: count, data: data }
        } catch (error) {
            throw error
        }
    }

    async default_agent_detail(default_agent_id: string, req: any) {
        try {

            let data = await this.Model.defaultAgentsModel.findOne({ _id: new Types.ObjectId(default_agent_id) }, {}, { lean: true })
            return data
        } catch (error) {
            throw error
        }
    }

    async chat_list(agent_id: string, body: dto.Agent.get_agent_chat) {
        try {
            const { pagination, limit, type, search } = body;

            const option = await this.CommonService.set_options(pagination, limit)
            const query = {
                agent_id: new Types.ObjectId(agent_id),
                ...(type && { type: type }),
                ...(search && {
                $or: [
                    { name: { $regex: search, $options: "i" } },
                    { email: { $regex: search, $options: "i" } },
                    { phone_no: { $regex: search, $options: "i" } }]
                })

            }
            const data = await this.Model.CallModel.find(query, {}, option).populate([{ path: 'agent_id', select: 'name image' }])
            const count = await this.Model.CallModel.countDocuments(query)
    
            return { 
                data, 
                count 
            }
        } catch (error) {
            throw error;
        }
    }

    async get_emotion_percentages_by_agent(agent_id: string) {
        try {
            const result = await this.Model.CallModel.aggregate([
                await agent_aggregation.match_with_agent_id(agent_id),
                {
                    $facet: {
                        tele_call: [
                            { $match: { type: "CALL" } },
                            { $group: agent_aggregation.emotionGroupStage() },
                            { $project: agent_aggregation.emotionProjectStage() }
                        ],
                        voice_call: [
                            { $match: { type: "VOICE_CHAT" } },
                            { $group: agent_aggregation.emotionGroupStage() },
                            { $project: agent_aggregation.emotionProjectStage() }
                        ],
                        chat: [
                            { $match: { type: "CHAT" } },
                            { $group: agent_aggregation.emotionGroupStage() },
                            { $project: agent_aggregation.emotionProjectStage() }
                        ]
                    }
                }
            ]);
    
            return {
                tele_call_emotion: result[0].tele_call[0] || { positive: 0, negative: 0, neutral: 0 },
                voice_call_emotion: result[0].voice_call[0] || { positive: 0, negative: 0, neutral: 0 },
                chat_emotion: result[0].chat[0] || { positive: 0, negative: 0, neutral: 0 }
            };
        } catch (error) {
            throw error;
        }
    }

    async defaultAgentWebUpdate(body: dto.Agent.websiteDefaultAgent) {
        try {
            const { call_prompt, call_first_message, chat_prompt, chat_first_message, voice, knowledge_base_id_for_website, ai_model } = body

            const agent = await this.Model.AgentModel.findOneAndUpdate(
                { is_default: true, default_type: "WEB" },
                {
                    call_prompt: call_prompt,
                    call_first_message: call_first_message, 
                    chat_prompt: chat_prompt,
                    chat_first_message: chat_first_message, 
                    voice: voice,
                    ai_model: ai_model,
                    knowledge_base_id_for_website: knowledge_base_id_for_website.length > 0 ? knowledge_base_id_for_website : [],
                    knowledge_base_id: knowledge_base_id_for_website.length > 0 ? knowledge_base_id_for_website : [],
                    default_type: "WEB",
                    name: "Qixs AI [Web]"
                }, 
                { upsert: true }
            );
            console.log('DEFAULT AGENT UPDATE (WEB) :', agent)

            return agent
        } catch (error) {
            throw error
        }
    }

    async getWebDefaultAgent() {
        try {
            const select = { 
                _id: 1,
                name: 1, 
                image: 1,
                voice: 1,
                ai_model: 1,
                is_default: 1,
                default_type: 1,
                call_first_message: 1, 
                chat_first_message: 1, 
                call_prompt: 1, 
                chat_prompt: 1, 
                knowledge_base_id_for_website: 1
            }

            const agent = await this.Model.AgentModel.findOne({ is_default: true, default_type: "WEB" }, select, { lean: true })
                .populate([{ path: 'knowledge_base_id_for_website' }]);
            return agent
        } catch (error) {
            throw error
        }
    }

    async agent_status_update(body: dto.Agent.agentStatusEdit) {
        try {
            const { status, agent_id } = body;
            console.log('status, agent_id', status, agent_id)
            const agent = await this.Model.AgentModel.findOne({ _id: new Types.ObjectId(agent_id) });
            if(!agent) throw new HttpException(
                `Invalid agent id! No agent was found matching the provided id`, 
                HttpStatus.BAD_REQUEST
            );

            await this.Model.AgentModel.findOneAndUpdate(
                { _id: new Types.ObjectId(agent_id) },
                { status: status },
                {  new: true }
            );

            await this.Model.scriptModel.findOneAndUpdate(
                { agent_id: new Types.ObjectId(agent_id) },
                { script_status: status },
                {  new: true }
            );

            return body;
        } catch (error) {
            throw error
        }
    }

    async delete_agent(agent_id: string) {
        try {
            const agent = await this.Model.AgentModel.findOne({ _id: new Types.ObjectId(agent_id), is_deleted: false });
            if(!agent) throw new HttpException(
                `Invalid agent id! No agent was found matching the provided id`, 
                HttpStatus.BAD_REQUEST
            );

            await this.Model.AgentModel.findOneAndUpdate(
                { _id: new Types.ObjectId(agent_id) },
                { is_deleted: true },
                {  new: true }
            );

            await this.Model.scriptModel.findOneAndDelete({ agent_id: new Types.ObjectId(agent_id) });

            return true;
        } catch (error) {
            throw error
        }
    }
}
