"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SupportBot = void 0;
const telegraf_1 = require("telegraf");
const types_js_1 = require("../../../domain/subscriptions/types.js");
const CALLBACK_PREFIX = {
    restart: "support:restart",
    button: "support:btn:",
    market: "support:market:",
    platform: "support:platform:",
    backToMarkets: "support:back:markets"
};
const MARKETS = [
    { code: "forex", label: "Forex" },
    { code: "crypto", label: "Crypto" },
    { code: "stocks", label: "Stocks" }
];
const CRYPTO_MARKET = "crypto";
const CRYPTO_PLATFORMS = types_js_1.PLATFORM_OPTIONS.filter((platform) => platform.code !== "forex" && platform.code !== "stocks");
class SupportBot {
    deps;
    bot;
    typingTimeouts = new Map();
    userStates = new Map();
    stateCleanupInterval;
    stateTtlMs = 30 * 60 * 1000;
    cleanupIntervalMs = 5 * 60 * 1000;
    constructor(deps) {
        this.deps = deps;
    }
    isEnabled() {
        return Boolean(this.deps.config.supportEnabled && this.deps.config.supportBotToken);
    }
    getBotUsername() {
        return this.deps.config.supportBotUsername;
    }
    getDeepLink() {
        const username = this.getBotUsername();
        return username ? `https://t.me/${username}?start=support` : undefined;
    }
    async start() {
        if (!this.isEnabled()) {
            this.deps.logger.info("Support bot is disabled.");
            return;
        }
        this.bot = new telegraf_1.Telegraf(this.deps.config.supportBotToken);
        this.bot.start(async (ctx) => {
            await this.handleStart(ctx);
        });
        this.registerCallbackHandlers();
        this.bot.on("message", async (ctx) => {
            await this.handleIncomingMessage(ctx);
        });
        this.bot.catch((error) => {
            this.deps.logger.error("Support bot unhandled error.", {
                error: error instanceof Error ? error.message : String(error),
                stack: error instanceof Error ? error.stack : undefined
            });
        });
        await this.bot.launch({ dropPendingUpdates: true });
        this.deps.logger.info("Support bot started.");
        this.startStateCleanup();
    }
    async stop() {
        this.stopStateCleanup();
        for (const timeout of this.typingTimeouts.values()) {
            clearTimeout(timeout);
        }
        this.typingTimeouts.clear();
        this.userStates.clear();
        await this.bot?.stop();
    }
    async sendMessageToUser(payload) {
        if (!this.bot)
            return;
        try {
            const chatId = payload.chatId;
            const extra = {};
            if (payload.inlineKeyboard?.length) {
                extra.reply_markup = {
                    inline_keyboard: payload.inlineKeyboard.map((button) => [
                        telegraf_1.Markup.button.callback(button.label, `${CALLBACK_PREFIX.button}${payload.sourceMessageId}:${button.id}`)
                    ])
                };
            }
            if (payload.telegramFileId) {
                switch (payload.messageType) {
                    case "PHOTO":
                        await this.bot.telegram.sendPhoto(chatId, payload.telegramFileId, {
                            caption: payload.text,
                            ...extra
                        });
                        break;
                    case "VIDEO":
                        await this.bot.telegram.sendVideo(chatId, payload.telegramFileId, {
                            caption: payload.text,
                            ...extra
                        });
                        break;
                    case "DOCUMENT":
                        await this.bot.telegram.sendDocument(chatId, payload.telegramFileId, {
                            caption: payload.text,
                            ...extra
                        });
                        break;
                    case "VOICE":
                        await this.bot.telegram.sendVoice(chatId, payload.telegramFileId, {
                            caption: payload.text,
                            ...extra
                        });
                        break;
                    default:
                        await this.bot.telegram.sendMessage(chatId, payload.text, extra);
                }
            }
            else {
                await this.bot.telegram.sendMessage(chatId, payload.text, extra);
            }
        }
        catch (error) {
            this.deps.logger.error("Failed to send support message to user.", {
                ticketId: payload.ticketId,
                chatId: payload.chatId,
                error: error instanceof Error ? error.message : String(error)
            });
        }
    }
    async sendTypingIndicator(chatId) {
        if (!this.bot)
            return;
        try {
            await this.bot.telegram.sendChatAction(chatId, "typing");
        }
        catch {
            // ignore
        }
    }
    registerCallbackHandlers() {
        if (!this.bot)
            return;
        this.bot.action(CALLBACK_PREFIX.restart, async (ctx) => {
            await ctx.answerCbQuery();
            await this.handleStart(ctx);
        });
        this.bot.action(new RegExp(`^${CALLBACK_PREFIX.button}([^:]+):([^:]+)$`), async (ctx) => {
            await ctx.answerCbQuery();
            const sourceMessageId = ctx.match[1];
            const buttonId = ctx.match[2];
            await this.handleButtonClick(ctx, sourceMessageId, buttonId);
        });
        this.bot.action(new RegExp(`^${CALLBACK_PREFIX.market}(.+)$`), async (ctx) => {
            await ctx.answerCbQuery();
            const marketCode = ctx.match[1];
            await this.handleMarketSelection(ctx, marketCode);
        });
        this.bot.action(new RegExp(`^${CALLBACK_PREFIX.platform}(.+)$`), async (ctx) => {
            await ctx.answerCbQuery();
            const platformCode = ctx.match[1];
            await this.handlePlatformSelection(ctx, platformCode);
        });
        this.bot.action(CALLBACK_PREFIX.backToMarkets, async (ctx) => {
            await ctx.answerCbQuery();
            await this.handleStart(ctx);
        });
    }
    async handleStart(ctx) {
        const from = ctx.from;
        if (!from)
            return;
        const telegramUserId = String(from.id);
        this.setUserState(telegramUserId, { type: "SELECTING_MARKET" });
        await this.sendMarketSelection(ctx, from);
    }
    async sendMarketSelection(ctx, from) {
        const text = this.getWelcomeMessage(from?.first_name);
        const buttons = MARKETS.map((market) => [
            telegraf_1.Markup.button.callback(market.label, `${CALLBACK_PREFIX.market}${market.code}`)
        ]);
        await ctx.reply(text, {
            parse_mode: "HTML",
            reply_markup: { inline_keyboard: buttons }
        });
    }
    async handleMarketSelection(ctx, marketCode) {
        const from = ctx.from;
        if (!from)
            return;
        const telegramUserId = String(from.id);
        const market = MARKETS.find((m) => m.code === marketCode);
        if (!market) {
            await ctx.reply("Please select a valid option.");
            return;
        }
        if (marketCode === CRYPTO_MARKET) {
            this.setUserState(telegramUserId, { type: "SELECTING_PLATFORM", market: market.label });
            await this.sendPlatformSelection(ctx, market.label);
        }
        else {
            this.setUserState(telegramUserId, { type: "WAITING_DETAILS", market: market.label });
            await ctx.reply(SupportBot.DETAILS_PROMPT, { parse_mode: "HTML" });
        }
    }
    async sendPlatformSelection(ctx, market) {
        const buttons = CRYPTO_PLATFORMS.map((platform) => [
            telegraf_1.Markup.button.callback(platform.name, `${CALLBACK_PREFIX.platform}${platform.code}`)
        ]);
        buttons.push([telegraf_1.Markup.button.callback("← Back to Markets", CALLBACK_PREFIX.backToMarkets)]);
        await ctx.reply(`You selected <b>${market}</b>. Please select the platform you need help with:`, {
            parse_mode: "HTML",
            reply_markup: { inline_keyboard: buttons }
        });
    }
    async handlePlatformSelection(ctx, platformCode) {
        const from = ctx.from;
        if (!from)
            return;
        const telegramUserId = String(from.id);
        const state = this.getUserState(telegramUserId);
        if (state?.type !== "SELECTING_PLATFORM")
            return;
        const platform = CRYPTO_PLATFORMS.find((p) => p.code === platformCode);
        if (!platform) {
            await ctx.reply("Please select a valid platform.");
            return;
        }
        this.setUserState(telegramUserId, {
            type: "WAITING_DETAILS",
            market: state.market,
            platform: platform.name
        });
        await ctx.reply(SupportBot.DETAILS_PROMPT, { parse_mode: "HTML" });
    }
    async handleIncomingMessage(ctx) {
        const from = ctx.from;
        const message = ctx.message;
        if (!from || !message)
            return;
        const telegramUserId = String(from.id);
        const chatId = String(ctx.chat?.id);
        const state = this.getUserState(telegramUserId);
        const input = this.extractMessageInput(from, message, chatId);
        if (!input)
            return;
        // No state: try to resume an existing open ticket, otherwise start onboarding.
        if (!state) {
            const existing = await this.deps.orchestrator.getOpenTicketForUser(input).catch((error) => {
                this.deps.logger.error("Failed to look up open support ticket.", {
                    telegramUserId,
                    error: error instanceof Error ? error.message : String(error)
                });
                return null;
            });
            if (existing?.ticket && existing.ticket.status !== "CLOSED" && existing.ticket.status !== "RESOLVED") {
                this.setUserState(telegramUserId, { type: "ACTIVE", ticketId: existing.ticket.id });
                await this.forwardUserMessage(ctx, input, telegramUserId);
                return;
            }
            await this.handleStart(ctx);
            return;
        }
        if (state.type === "SELECTING_MARKET" || state.type === "SELECTING_PLATFORM") {
            await ctx.reply("Please use the buttons above to make a selection.");
            return;
        }
        if (state.type === "WAITING_DETAILS") {
            if (!input.text) {
                await ctx.reply("Please send a text message with your request details.");
                return;
            }
            await this.createTicketFromOnboarding(ctx, input, state.market, state.platform);
            return;
        }
        // ACTIVE state: forward the message to the existing ticket.
        await this.forwardUserMessage(ctx, input, telegramUserId);
    }
    async forwardUserMessage(ctx, input, telegramUserId) {
        await this.sendTypingIndicator(input.chatId);
        try {
            await this.deps.orchestrator.handleUserMessage(input);
        }
        catch (error) {
            this.deps.logger.error("Failed to handle support message.", {
                telegramUserId,
                error: error instanceof Error ? error.message : String(error)
            });
            await ctx.reply("Sorry, something went wrong. Please try again shortly.");
        }
    }
    async createTicketFromOnboarding(ctx, input, market, platform) {
        const from = ctx.from;
        if (!from)
            return;
        const telegramUserId = String(from.id);
        try {
            const result = await this.deps.orchestrator.createTicketFromOnboarding(input, market, platform, input.text ?? "");
            this.setUserState(telegramUserId, { type: "ACTIVE", ticketId: result.ticket.id });
            await ctx.reply(this.getTicketCreatedConfirmation(result.ticket.ticketNumber), {
                parse_mode: "HTML"
            });
        }
        catch (error) {
            this.deps.logger.error("Failed to create ticket from onboarding.", {
                telegramUserId,
                error: error instanceof Error ? error.message : String(error)
            });
            await ctx.reply("Sorry, we couldn't create your ticket right now. Please try again shortly.");
        }
    }
    async handleButtonClick(ctx, sourceMessageId, buttonId) {
        const from = ctx.from;
        const chatId = String(ctx.chat?.id);
        if (!from)
            return;
        try {
            // Remove the keyboard from the original message to indicate selection.
            if (ctx.callbackQuery?.message) {
                await ctx.editMessageReplyMarkup({ inline_keyboard: [] });
            }
        }
        catch {
            // ignore edit failures
        }
        try {
            await this.deps.orchestrator.recordButtonSelection({
                telegramUserId: String(from.id),
                chatId,
                username: from.username,
                firstName: from.first_name,
                lastName: from.last_name,
                language: from.language_code,
                messageType: "TEXT"
            }, sourceMessageId, buttonId);
        }
        catch (error) {
            this.deps.logger.error("Failed to handle button click.", {
                sourceMessageId,
                buttonId,
                error: error instanceof Error ? error.message : String(error)
            });
        }
    }
    extractMessageInput(from, message, chatId) {
        if (!from)
            return null;
        const base = {
            telegramUserId: String(from.id),
            chatId,
            username: from.username,
            firstName: from.first_name,
            lastName: from.last_name,
            language: from.language_code,
            telegramMessageId: message.message_id
        };
        if ("text" in message && message.text) {
            return { ...base, messageType: "TEXT", text: message.text };
        }
        if ("photo" in message && message.photo && message.photo.length > 0) {
            const largest = message.photo[message.photo.length - 1];
            return {
                ...base,
                messageType: "PHOTO",
                text: "caption" in message ? message.caption : undefined,
                telegramFileId: largest.file_id,
                metadata: { width: largest.width, height: largest.height, fileSize: largest.file_size }
            };
        }
        if ("video" in message && message.video) {
            return {
                ...base,
                messageType: "VIDEO",
                text: "caption" in message ? message.caption : undefined,
                telegramFileId: message.video.file_id,
                metadata: {
                    width: message.video.width,
                    height: message.video.height,
                    duration: message.video.duration,
                    fileSize: message.video.file_size,
                    mimeType: message.video.mime_type
                }
            };
        }
        if ("document" in message && message.document) {
            return {
                ...base,
                messageType: "DOCUMENT",
                text: "caption" in message ? message.caption : undefined,
                telegramFileId: message.document.file_id,
                metadata: {
                    fileName: message.document.file_name,
                    mimeType: message.document.mime_type,
                    fileSize: message.document.file_size
                }
            };
        }
        if ("voice" in message && message.voice) {
            return {
                ...base,
                messageType: "VOICE",
                telegramFileId: message.voice.file_id,
                metadata: { duration: message.voice.duration, fileSize: message.voice.file_size }
            };
        }
        if ("audio" in message && message.audio) {
            return {
                ...base,
                messageType: "AUDIO",
                text: "caption" in message ? message.caption : undefined,
                telegramFileId: message.audio.file_id,
                metadata: {
                    duration: message.audio.duration,
                    fileSize: message.audio.file_size,
                    mimeType: message.audio.mime_type
                }
            };
        }
        if ("sticker" in message && message.sticker) {
            return {
                ...base,
                messageType: "STICKER",
                telegramFileId: message.sticker.file_id,
                metadata: { emoji: message.sticker.emoji, isAnimated: message.sticker.is_animated }
            };
        }
        if ("animation" in message && message.animation) {
            return {
                ...base,
                messageType: "ANIMATION",
                telegramFileId: message.animation.file_id,
                metadata: { duration: message.animation.duration, fileSize: message.animation.file_size }
            };
        }
        if ("location" in message && message.location) {
            return {
                ...base,
                messageType: "LOCATION",
                metadata: { latitude: message.location.latitude, longitude: message.location.longitude }
            };
        }
        if ("contact" in message && message.contact) {
            return {
                ...base,
                messageType: "CONTACT",
                metadata: {
                    contactName: `${message.contact.first_name} ${message.contact.last_name ?? ""}`.trim(),
                    contactPhone: message.contact.phone_number,
                    contactUserId: message.contact.user_id
                }
            };
        }
        return { ...base, messageType: "TEXT", text: "Unsupported message type." };
    }
    getWelcomeMessage(firstName) {
        return `👋 <b>Welcome to Quant Whisper Support!</b>

Thank you ${firstName ?? "there"} for contacting <b>Quant Whisper Support</b>. We're here to help you get the most out of the Quant Whisper platform.

Whether you have a question, need assistance with your account, want to report an issue, suggest a new feature, or require technical support, our team is ready to assist you.

To ensure we provide the fastest and most accurate assistance, your request will be organized into a support ticket. Depending on your inquiry, our AI assistant may provide an immediate solution or connect you with a human support specialist when needed.

<b>We can help you with:</b>
• 📈 General Questions
• 🛠 Technical Issues & Bug Reports
• 💳 Account & Payment Assistance
• 💡 Feature Requests & Suggestions
• 🚨 Report a Problem
• 📞 Speak with a Human Support Agent

Please select one of the service options below to get started.`;
    }
    static DETAILS_PROMPT = "To help us understand your issue and provide the best possible assistance, please describe your request in as much detail as possible.\n\n" +
        "When applicable, include:\n" +
        "• A clear description of the issue or question.\n" +
        "• The steps you took before the issue occurred.\n" +
        "• Any error messages or unexpected behavior you experienced.\n" +
        "• Screenshots, screen recordings, or relevant documents that may help us investigate.\n\n" +
        "Once you send your message, a support ticket will be created automatically. Our AI assistant may respond immediately if a solution is available. " +
        "If additional assistance is required, your ticket will be forwarded to a human support agent.\n\n" +
        "We appreciate your patience and look forward to helping you.";
    getTicketCreatedConfirmation(ticketNumber) {
        return `Thank you for your submission. Your support ticket <b>${ticketNumber}</b> has been created. A support agent will assist you shortly.`;
    }
    setUserState(telegramUserId, state) {
        this.userStates.set(telegramUserId, { ...state, lastActivityAt: Date.now() });
    }
    getUserState(telegramUserId) {
        const state = this.userStates.get(telegramUserId);
        if (!state)
            return undefined;
        if (Date.now() - state.lastActivityAt > this.stateTtlMs) {
            this.userStates.delete(telegramUserId);
            return undefined;
        }
        return state;
    }
    startStateCleanup() {
        if (this.stateCleanupInterval)
            return;
        this.stateCleanupInterval = setInterval(() => {
            this.cleanupExpiredStates();
        }, this.cleanupIntervalMs);
        this.stateCleanupInterval.unref();
    }
    stopStateCleanup() {
        if (this.stateCleanupInterval) {
            clearInterval(this.stateCleanupInterval);
            this.stateCleanupInterval = undefined;
        }
    }
    cleanupExpiredStates() {
        const now = Date.now();
        for (const [telegramUserId, state] of this.userStates.entries()) {
            if (now - state.lastActivityAt > this.stateTtlMs) {
                this.userStates.delete(telegramUserId);
            }
        }
    }
}
exports.SupportBot = SupportBot;
