"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SupportOrchestrator = void 0;
const mongoose_1 = __importDefault(require("mongoose"));
class SupportOrchestrator {
    userService;
    ticketService;
    messageService;
    agentService;
    settingsService;
    assignmentService;
    aiProvider;
    eventBus;
    config;
    logger;
    sendToUser;
    constructor(userService, ticketService, messageService, agentService, settingsService, assignmentService, aiProvider, eventBus, config, logger, sendToUser) {
        this.userService = userService;
        this.ticketService = ticketService;
        this.messageService = messageService;
        this.agentService = agentService;
        this.settingsService = settingsService;
        this.assignmentService = assignmentService;
        this.aiProvider = aiProvider;
        this.eventBus = eventBus;
        this.config = config;
        this.logger = logger;
        this.sendToUser = sendToUser;
    }
    async openChat(input) {
        try {
            const user = await this.userService.findOrCreate({
                telegramUserId: input.telegramUserId,
                chatId: input.chatId,
                username: input.username,
                firstName: input.firstName,
                lastName: input.lastName,
                language: input.language
            });
            let ticket = await this.ticketService.getOpenTicketByUserId(user.id);
            let isNew = false;
            if (!ticket) {
                ticket = await this.ticketService.createTicket(user.id, "[Chat opened]", undefined, "NORMAL");
                this.eventBus.emit("ticket:created", ticket);
                await this.maybeSendTicketOpenMessage(ticket, user);
                isNew = true;
            }
            return { user, ticket, isNew };
        }
        catch (error) {
            this.logger.error("Failed to open chat.", { error: error instanceof Error ? error.message : String(error), telegramUserId: input.telegramUserId });
            return null;
        }
    }
    async getOpenTicketForUser(input) {
        try {
            const user = await this.userService.findOrCreate({
                telegramUserId: input.telegramUserId,
                chatId: input.chatId,
                username: input.username,
                firstName: input.firstName,
                lastName: input.lastName,
                language: input.language
            });
            const ticket = await this.ticketService.getOpenTicketByUserId(user.id);
            if (!ticket)
                return null;
            return { user, ticket };
        }
        catch (error) {
            this.logger.error("Failed to get open ticket for user.", {
                error: error instanceof Error ? error.message : String(error),
                telegramUserId: input.telegramUserId
            });
            return null;
        }
    }
    async handleUserMessage(input) {
        const user = await this.userService.findOrCreate({
            telegramUserId: input.telegramUserId,
            chatId: input.chatId,
            username: input.username,
            firstName: input.firstName,
            lastName: input.lastName,
            language: input.language
        });
        let ticket = await this.ticketService.getOpenTicketByUserId(user.id);
        if (!ticket) {
            ticket = await this.ticketService.createTicket(user.id, input.text ?? `[${input.messageType}]`, undefined, "NORMAL");
            this.eventBus.emit("ticket:created", ticket);
            await this.maybeSendTicketOpenMessage(ticket, user);
        }
        const message = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "USER",
            senderId: user.telegramUserId,
            messageType: input.messageType,
            text: input.text,
            telegramMessageId: input.telegramMessageId,
            telegramFileId: input.telegramFileId,
            metadata: input.metadata
        });
        this.eventBus.emit("message:created", { message, ticket });
        const settings = await this.settingsService.getSettings();
        let aiResponse;
        let escalated = false;
        const history = await this.getRecentMessageTexts(ticket.id);
        const analysis = await this.aiProvider.analyzeMessage(input.text ?? "", history);
        if (analysis.shouldEscalate) {
            escalated = true;
            const previousStatus = ticket.status;
            ticket = (await this.ticketService.updateStatus(ticket.id, "WAITING_AGENT")) ?? ticket;
            if (previousStatus !== ticket.status) {
                this.eventBus.emit("ticket:statusChanged", { ticket, previousStatus });
            }
            aiResponse = "I'm connecting you with a human support agent. Please hold on.";
        }
        else if (settings.aiEnabled &&
            analysis.confidence >= settings.aiConfidenceThreshold &&
            analysis.response) {
            aiResponse = analysis.response;
            ticket = (await this.ticketService.updateCategory(ticket.id, analysis.suggestedCategory ?? "General")) ?? ticket;
            if (analysis.suggestedPriority) {
                ticket = (await this.ticketService.updatePriority(ticket.id, analysis.suggestedPriority)) ?? ticket;
            }
        }
        else if (settings.aiEnabled) {
            escalated = true;
            const previousStatus = ticket.status;
            ticket = (await this.ticketService.updateStatus(ticket.id, "WAITING_AGENT")) ?? ticket;
            if (previousStatus !== ticket.status) {
                this.eventBus.emit("ticket:statusChanged", { ticket, previousStatus });
            }
            aiResponse = "I'm not sure about that. Let me get a human agent to help you.";
        }
        else {
            escalated = true;
            const previousStatus = ticket.status;
            ticket = (await this.ticketService.updateStatus(ticket.id, "WAITING_AGENT")) ?? ticket;
            if (previousStatus !== ticket.status) {
                this.eventBus.emit("ticket:statusChanged", { ticket, previousStatus });
            }
        }
        if (aiResponse) {
            await this.sendBotReply(ticket, user, aiResponse);
        }
        return { user, ticket, message, aiResponse, escalated };
    }
    async createTicketFromOnboarding(input, market, platform, details) {
        const user = await this.userService.findOrCreate({
            telegramUserId: input.telegramUserId,
            chatId: input.chatId,
            username: input.username,
            firstName: input.firstName,
            lastName: input.lastName,
            language: input.language
        });
        const category = platform ? `${market} / ${platform}` : market;
        const ticket = await this.ticketService.createTicket(user.id, details, category, "NORMAL");
        this.eventBus.emit("ticket:created", ticket);
        const systemMessage = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "SYSTEM",
            senderId: "system",
            messageType: "TEXT",
            text: `Ticket opened via Support Bot. Market: ${market}${platform ? `, Platform: ${platform}` : ""}.`
        });
        this.eventBus.emit("message:created", { message: systemMessage, ticket });
        const message = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "USER",
            senderId: user.telegramUserId,
            messageType: "TEXT",
            text: details,
            telegramMessageId: input.telegramMessageId
        });
        this.eventBus.emit("message:created", { message, ticket });
        const previousStatus = ticket.status;
        const updatedTicket = await this.ticketService.updateStatus(ticket.id, "WAITING_AGENT");
        if (updatedTicket && previousStatus !== updatedTicket.status) {
            this.eventBus.emit("ticket:statusChanged", { ticket: updatedTicket, previousStatus });
        }
        return { user, ticket: updatedTicket ?? ticket, message };
    }
    async handleAgentReply(input) {
        let ticket = await this.ticketService.getById(input.ticketId);
        if (!ticket)
            throw new Error(`Ticket not found: ${input.ticketId}`);
        const user = await this.userService.getById(ticket.userId);
        if (!user)
            throw new Error(`User not found for ticket: ${input.ticketId}`);
        // Resolve the support agent document ID from the admin user ID.
        const agent = await this.agentService.getByAdminUserId(input.agentId);
        const agentId = agent?.id ?? input.agentId;
        const message = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "AGENT",
            senderId: input.agentId,
            messageType: input.messageType ?? "TEXT",
            text: input.text,
            telegramFileId: input.telegramFileId,
            metadata: input.metadata
        });
        if (ticket.status === "OPEN" || ticket.status === "WAITING_AGENT") {
            const previousStatus = ticket.status;
            ticket = (await this.ticketService.updateStatus(ticket.id, "ACTIVE")) ?? ticket;
            if (previousStatus !== ticket.status) {
                this.eventBus.emit("ticket:statusChanged", { ticket, previousStatus, agentId: input.agentId });
            }
        }
        if (!ticket.assignedAgent) {
            ticket = (await this.ticketService.assignTicket(ticket.id, agentId)) ?? ticket;
            this.eventBus.emit("ticket:assigned", { ticket, agentId });
        }
        this.eventBus.emit("message:created", { message, ticket });
        if (this.sendToUser) {
            await this.sendToUser({
                ticketId: ticket.id,
                chatId: user.chatId,
                text: input.text,
                messageType: input.messageType ?? "TEXT",
                telegramFileId: input.telegramFileId,
                metadata: input.metadata
            });
        }
        return { ticket, message };
    }
    async assignTicketToAgent(ticketId, agentId) {
        const ticket = await this.ticketService.assignTicket(ticketId, agentId);
        if (ticket) {
            await this.agentService.incrementCurrentTickets(agentId);
            this.eventBus.emit("ticket:assigned", { ticket, agentId });
        }
        return ticket;
    }
    async closeTicket(ticketId, agentId) {
        const ticket = await this.ticketService.updateStatus(ticketId, "CLOSED", agentId);
        if (ticket && ticket.assignedAgent) {
            const agentObjectId = await this.resolveAgentObjectId(ticket.assignedAgent);
            if (agentObjectId) {
                await this.agentService.decrementCurrentTickets(agentObjectId);
            }
        }
        if (ticket) {
            this.eventBus.emit("ticket:closed", { ticket, agentId });
            await this.maybeSendTicketCloseMessage(ticket);
        }
        return ticket;
    }
    async resolveTicket(ticketId, agentId) {
        const ticket = await this.ticketService.updateStatus(ticketId, "RESOLVED", agentId);
        if (ticket && ticket.assignedAgent) {
            const agentObjectId = await this.resolveAgentObjectId(ticket.assignedAgent);
            if (agentObjectId) {
                await this.agentService.decrementCurrentTickets(agentObjectId);
            }
        }
        if (ticket) {
            this.eventBus.emit("ticket:closed", { ticket, agentId });
            await this.maybeSendTicketCloseMessage(ticket);
        }
        return ticket;
    }
    async resolveAgentObjectId(assignedAgent) {
        if (mongoose_1.default.isValidObjectId(assignedAgent)) {
            return assignedAgent;
        }
        const agent = await this.agentService.getByAdminUserId(assignedAgent);
        return agent?.id ?? null;
    }
    async maybeSendTicketOpenMessage(ticket, user) {
        if (!this.sendToUser)
            return;
        try {
            const settings = await this.settingsService.getSettings();
            if (!settings.autoMessageOnTicketOpenEnabled)
                return;
            const text = this.formatAutoMessage(settings.autoMessageOnTicketOpenText, ticket, user);
            await this.sendAutoMessage(ticket, user, text, settings.autoMessageOnTicketOpenButtons);
        }
        catch (error) {
            this.logger.error("Failed to send ticket open auto-message.", { error: error instanceof Error ? error.message : String(error), ticketId: ticket.id });
        }
    }
    async maybeSendTicketCloseMessage(ticket) {
        if (!this.sendToUser)
            return;
        try {
            const settings = await this.settingsService.getSettings();
            if (!settings.autoMessageOnTicketCloseEnabled)
                return;
            const user = await this.userService.getById(ticket.userId);
            if (!user)
                return;
            const text = this.formatAutoMessage(settings.autoMessageOnTicketCloseText, ticket, user);
            await this.sendAutoMessage(ticket, user, text, settings.autoMessageOnTicketCloseButtons);
        }
        catch (error) {
            this.logger.error("Failed to send ticket close auto-message.", { error: error instanceof Error ? error.message : String(error), ticketId: ticket.id });
        }
    }
    async sendAutoMessage(ticket, user, text, buttons) {
        if (!this.sendToUser)
            return;
        const outgoingMessage = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "SYSTEM",
            senderId: "system",
            messageType: "TEXT",
            text,
            metadata: buttons?.length ? { inlineKeyboard: buttons } : undefined
        });
        this.eventBus.emit("message:created", { message: outgoingMessage, ticket });
        await this.sendToUser({
            ticketId: ticket.id,
            chatId: user.chatId,
            text,
            messageType: "TEXT",
            inlineKeyboard: buttons,
            sourceMessageId: outgoingMessage.id
        });
    }
    async recordButtonSelection(input, sourceMessageId, buttonId) {
        try {
            const user = await this.userService.findOrCreate({
                telegramUserId: input.telegramUserId,
                chatId: input.chatId,
                username: input.username,
                firstName: input.firstName,
                lastName: input.lastName,
                language: input.language
            });
            const ticket = await this.ticketService.getOpenTicketByUserId(user.id);
            if (!ticket) {
                this.logger.warn("No open ticket for button selection.", { sourceMessageId, buttonId, userId: user.id });
                return null;
            }
            const sourceMessages = await this.messageService.getMessagesByTicketId(ticket.id, 200);
            const original = sourceMessages.find((m) => m.id === sourceMessageId);
            const buttons = original?.metadata?.inlineKeyboard;
            const button = buttons?.find((b) => b.id === buttonId);
            if (!button) {
                this.logger.warn("Button not found for callback.", { sourceMessageId, buttonId });
                return null;
            }
            const message = await this.messageService.sendMessage({
                ticketId: ticket.id,
                senderType: "USER",
                senderId: input.telegramUserId,
                messageType: "TEXT",
                text: `Selected: ${button.label}`,
                metadata: {
                    buttonClick: true,
                    sourceMessageId,
                    buttonId,
                    selectedLabel: button.label,
                    selectedValue: button.value
                }
            });
            this.eventBus.emit("message:created", { message, ticket });
            return message;
        }
        catch (error) {
            this.logger.error("Failed to record button selection.", { error: error instanceof Error ? error.message : String(error), sourceMessageId, buttonId });
            return null;
        }
    }
    formatAutoMessage(template, ticket, user) {
        return template
            .replace(/{{ticketNumber}}/g, ticket.ticketNumber)
            .replace(/{{firstName}}/g, user.firstName ?? "there")
            .replace(/{{ticketStatus}}/g, ticket.status);
    }
    async markMessagesAsReadByAgent(ticketId) {
        const count = await this.messageService.markAsReadByAgent(ticketId);
        if (count > 0) {
            this.eventBus.emit("message:read", { ticketId, readBy: "agent", messageIds: [] });
        }
    }
    async sendSystemMessage(ticketId, text) {
        const ticket = await this.ticketService.getById(ticketId);
        if (!ticket)
            throw new Error(`Ticket not found: ${ticketId}`);
        const message = await this.messageService.sendMessage({
            ticketId,
            senderType: "SYSTEM",
            senderId: "system",
            messageType: "TEXT",
            text
        });
        this.eventBus.emit("message:created", { message, ticket });
        return message;
    }
    async sendBotReply(ticket, user, text) {
        const message = await this.messageService.sendMessage({
            ticketId: ticket.id,
            senderType: "BOT",
            senderId: "bot",
            messageType: "TEXT",
            text
        });
        this.eventBus.emit("message:created", { message, ticket });
        if (this.sendToUser) {
            await this.sendToUser({
                ticketId: ticket.id,
                chatId: user.chatId,
                text,
                messageType: "TEXT"
            });
        }
        return message;
    }
    async getRecentMessageTexts(ticketId) {
        const messages = await this.messageService.getMessagesByTicketId(ticketId, 10);
        return messages.map((m) => m.text ?? `[${m.messageType}]`).filter(Boolean);
    }
}
exports.SupportOrchestrator = SupportOrchestrator;
