"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
// --- Mocks ---
vitest_1.vi.mock('../../utils/index.js', () => ({
    createModuleLogger: () => ({
        info: vitest_1.vi.fn(),
        warn: vitest_1.vi.fn(),
        error: vitest_1.vi.fn(),
        debug: vitest_1.vi.fn(),
    }),
}));
const mockCronTask = { stop: vitest_1.vi.fn() };
vitest_1.vi.mock('node-cron', () => ({
    default: {
        schedule: vitest_1.vi.fn((_expr, cb) => {
            mockCronTask._callback = cb;
            return mockCronTask;
        }),
    },
}));
vitest_1.vi.mock('../timer-system/index.js', () => ({
    calculateDelay: vitest_1.vi.fn(() => 2),
    DEFAULT_TIMER_CONFIG: {
        baseDelay: { min: 5, max: 30 },
        busyScenarioDelay: { min: 15, max: 45 },
        typingSpeedCps: 5,
        nightHours: { start: 23, end: 7 },
        nightMultiplier: 2.5,
    },
}));
const node_cron_1 = __importDefault(require("node-cron"));
const index_js_1 = require("./index.js");
// --- Mock factories ---
function createMockCharacterEngine() {
    return {
        getCharacter: vitest_1.vi.fn().mockResolvedValue({
            id: 'char-1',
            status: 'active',
            identity: { name: 'TestChar' },
        }),
        listCharacters: vitest_1.vi.fn().mockResolvedValue([
            { id: 'char-1', status: 'active' },
            { id: 'char-2', status: 'inactive' },
        ]),
    };
}
function createMockConversationEngine() {
    return {
        handleIncomingMessage: vitest_1.vi.fn().mockResolvedValue(undefined),
    };
}
function createMockImageEngine() {
    return {
        generateImage: vitest_1.vi.fn().mockResolvedValue('task-123'),
        templateManager: {
            listTemplates: vitest_1.vi.fn().mockResolvedValue([
                { id: 'tpl-1', name: 'Beach' },
                { id: 'tpl-2', name: 'Cafe' },
            ]),
        },
    };
}
function createMockBotClient() {
    return {
        sendChatAction: vitest_1.vi.fn().mockResolvedValue(undefined),
        sendMessage: vitest_1.vi.fn().mockResolvedValue(undefined),
        createChannel: vitest_1.vi.fn().mockResolvedValue({ chatId: 100, inviteLink: 'https://t.me/test' }),
        getChatMemberCount: vitest_1.vi.fn().mockResolvedValue(0),
        sendPhoto: vitest_1.vi.fn().mockResolvedValue(undefined),
        sendPaidMedia: vitest_1.vi.fn().mockResolvedValue(undefined),
    };
}
function createMockChannelManager() {
    return {
        createChannels: vitest_1.vi.fn().mockResolvedValue({
            characterId: 'char-1',
            normalChannel: { chatId: 100 },
            vipChannel: { chatId: 200 },
        }),
    };
}
function createMockStarsManager() {
    return {
        handleSuccessfulPayment: vitest_1.vi.fn().mockResolvedValue({ id: 'tx-1' }),
    };
}
function createMockTelegramManager(botClient) {
    const client = botClient || createMockBotClient();
    const channelMgr = createMockChannelManager();
    const starsMgr = createMockStarsManager();
    return {
        getBotClient: vitest_1.vi.fn().mockReturnValue(client),
        getChannelManager: vitest_1.vi.fn().mockReturnValue(channelMgr),
        getStarsManager: vitest_1.vi.fn().mockReturnValue(starsMgr),
        _botClient: client,
        _channelMgr: channelMgr,
        _starsMgr: starsMgr,
    };
}
function createMockQueueSystem() {
    return {
        add: vitest_1.vi.fn().mockResolvedValue({ id: 'q-1' }),
    };
}
function createMockAnalyticsEngine() {
    return {
        calculateDailyAnalytics: vitest_1.vi.fn().mockResolvedValue({}),
    };
}
const DEFAULT_TIMER = {
    baseDelay: { min: 5, max: 30 },
    busyScenarioDelay: { min: 15, max: 45 },
    typingSpeedCps: 5,
    nightHours: { start: 23, end: 7 },
    nightMultiplier: 2.5,
};
function createManager(overrides = {}) {
    const charEngine = overrides.characterEngine || createMockCharacterEngine();
    const convEngine = overrides.conversationEngine || createMockConversationEngine();
    const imgEngine = overrides.imageEngine || createMockImageEngine();
    const telMgr = overrides.telegramManager || createMockTelegramManager();
    const queueSys = overrides.queueSystem || createMockQueueSystem();
    const timer = overrides.timerConfig || DEFAULT_TIMER;
    const analytics = overrides.analyticsEngine || createMockAnalyticsEngine();
    return {
        manager: new index_js_1.AutomationManager(charEngine, convEngine, imgEngine, telMgr, queueSys, timer, analytics),
        charEngine,
        convEngine,
        imgEngine,
        telMgr,
        queueSys,
        analytics,
    };
}
// =============================================
// Tests
// =============================================
let ctx;
(0, vitest_1.beforeEach)(() => {
    vitest_1.vi.clearAllMocks();
    vitest_1.vi.useFakeTimers();
    ctx = createManager();
});
(0, vitest_1.afterEach)(() => {
    ctx.manager.stopAutomation();
    vitest_1.vi.useRealTimers();
});
(0, vitest_1.describe)('AutomationManager — Status management', () => {
    (0, vitest_1.it)('should default to inactive for unknown characters', () => {
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('unknown')).toBe('inactive');
    });
    (0, vitest_1.it)('should set and get automation status', () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('active');
    });
    (0, vitest_1.it)('should track error status', () => {
        ctx.manager.setAutomationStatus('char-1', 'error');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('error');
    });
    (0, vitest_1.it)('should return all statuses', () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.manager.setAutomationStatus('char-2', 'inactive');
        const all = ctx.manager.getAllStatuses();
        (0, vitest_1.expect)(all.get('char-1')).toBe('active');
        (0, vitest_1.expect)(all.get('char-2')).toBe('inactive');
        (0, vitest_1.expect)(all.size).toBe(2);
    });
});
(0, vitest_1.describe)('AutomationManager — Character lifecycle', () => {
    (0, vitest_1.it)('onCharacterCreated should set status to active and create channels', async () => {
        await ctx.manager.onCharacterCreated('char-1');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('active');
        (0, vitest_1.expect)(ctx.telMgr.getBotClient).toHaveBeenCalledWith('char-1');
        (0, vitest_1.expect)(ctx.telMgr._channelMgr.createChannels).toHaveBeenCalledWith('char-1', vitest_1.expect.anything(), 'TestChar — Channel', 'TestChar — VIP');
    });
    (0, vitest_1.it)('onCharacterCreated should set error status on failure', async () => {
        ctx.telMgr._channelMgr.createChannels.mockRejectedValue(new Error('API error'));
        await ctx.manager.onCharacterCreated('char-1');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('error');
    });
    (0, vitest_1.it)('onCharacterCreated should skip channels when no bot client', async () => {
        ctx.telMgr.getBotClient.mockReturnValue(undefined);
        await ctx.manager.onCharacterCreated('char-1');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('active');
        (0, vitest_1.expect)(ctx.telMgr._channelMgr.createChannels).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)('onCharacterActivated should set status to active', async () => {
        await ctx.manager.onCharacterActivated('char-1');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('active');
    });
    (0, vitest_1.it)('onCharacterDeactivated should set status to inactive', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        await ctx.manager.onCharacterDeactivated('char-1');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('inactive');
    });
});
(0, vitest_1.describe)('AutomationManager — Message handling', () => {
    (0, vitest_1.it)('should pass message to conversation engine when active', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        await ctx.manager.handleIncomingMessage('char-1', 42, 'hello');
        (0, vitest_1.expect)(ctx.convEngine.handleIncomingMessage).toHaveBeenCalledWith('char-1', 42, 'hello');
    });
    (0, vitest_1.it)('should ignore messages when automation is not active', async () => {
        // Default status is inactive
        await ctx.manager.handleIncomingMessage('char-1', 42, 'hello');
        (0, vitest_1.expect)(ctx.convEngine.handleIncomingMessage).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)('should send typing indicator after partial delay', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        await ctx.manager.handleIncomingMessage('char-1', 42, 'hello');
        // Advance timers to trigger the typing setTimeout
        await vitest_1.vi.advanceTimersByTimeAsync(1500);
        (0, vitest_1.expect)(ctx.telMgr._botClient.sendChatAction).toHaveBeenCalledWith(42, 'typing');
    });
    (0, vitest_1.it)('should set error status on conversation engine failure', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.convEngine.handleIncomingMessage.mockRejectedValue(new Error('AI error'));
        await ctx.manager.handleIncomingMessage('char-1', 42, 'hello');
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('error');
    });
});
(0, vitest_1.describe)('AutomationManager — Stars payment', () => {
    const mockPayment = {
        currency: 'XTR',
        total_amount: 50,
        invoice_payload: '{"imageId":"img-1"}',
        telegram_payment_charge_id: 'charge-1',
        provider_payment_charge_id: 'provider-1',
    };
    (0, vitest_1.it)('should process payment when active', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        await ctx.manager.handleStarsPayment('char-1', 42, mockPayment);
        (0, vitest_1.expect)(ctx.telMgr._starsMgr.handleSuccessfulPayment).toHaveBeenCalledWith('char-1', mockPayment, 42, vitest_1.expect.anything());
    });
    (0, vitest_1.it)('should ignore payment when not active', async () => {
        await ctx.manager.handleStarsPayment('char-1', 42, mockPayment);
        (0, vitest_1.expect)(ctx.telMgr._starsMgr.handleSuccessfulPayment).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)('should set error status on payment failure', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.telMgr._starsMgr.handleSuccessfulPayment.mockRejectedValue(new Error('Payment error'));
        await ctx.manager.handleStarsPayment('char-1', 42, mockPayment);
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('error');
    });
    (0, vitest_1.it)('should throw when no bot client for payment', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.telMgr.getBotClient.mockReturnValue(undefined);
        await ctx.manager.handleStarsPayment('char-1', 42, mockPayment);
        (0, vitest_1.expect)(ctx.manager.getAutomationStatus('char-1')).toBe('error');
    });
});
(0, vitest_1.describe)('AutomationManager — Content cycle', () => {
    (0, vitest_1.it)('should generate image for active character', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        await ctx.manager.runContentCycle('char-1');
        (0, vitest_1.expect)(ctx.imgEngine.templateManager.listTemplates).toHaveBeenCalled();
        (0, vitest_1.expect)(ctx.imgEngine.generateImage).toHaveBeenCalledWith('char-1', vitest_1.expect.any(String));
    });
    (0, vitest_1.it)('should skip content cycle when not active', async () => {
        await ctx.manager.runContentCycle('char-1');
        (0, vitest_1.expect)(ctx.imgEngine.generateImage).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)('should handle no templates gracefully', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.imgEngine.templateManager.listTemplates.mockResolvedValue([]);
        await ctx.manager.runContentCycle('char-1');
        (0, vitest_1.expect)(ctx.imgEngine.generateImage).not.toHaveBeenCalled();
    });
    (0, vitest_1.it)('should not throw on image generation failure', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        ctx.imgEngine.generateImage.mockRejectedValue(new Error('KIE error'));
        // Should not throw
        await ctx.manager.runContentCycle('char-1');
    });
});
(0, vitest_1.describe)('AutomationManager — Scheduling', () => {
    (0, vitest_1.it)('should start automation with default cron expression', () => {
        ctx.manager.startAutomation();
        (0, vitest_1.expect)(node_cron_1.default.schedule).toHaveBeenCalledWith('0 */4 * * *', vitest_1.expect.any(Function));
        (0, vitest_1.expect)(ctx.manager.isRunning()).toBe(true);
    });
    (0, vitest_1.it)('should start automation with custom cron expression', () => {
        ctx.manager.startAutomation('0 */2 * * *');
        (0, vitest_1.expect)(node_cron_1.default.schedule).toHaveBeenCalledWith('0 */2 * * *', vitest_1.expect.any(Function));
        (0, vitest_1.expect)(ctx.manager.isRunning()).toBe(true);
    });
    (0, vitest_1.it)('should stop previous cron task when restarting', () => {
        ctx.manager.startAutomation();
        ctx.manager.startAutomation('0 */6 * * *');
        (0, vitest_1.expect)(mockCronTask.stop).toHaveBeenCalled();
    });
    (0, vitest_1.it)('should stop automation', () => {
        ctx.manager.startAutomation();
        ctx.manager.stopAutomation();
        (0, vitest_1.expect)(mockCronTask.stop).toHaveBeenCalled();
        (0, vitest_1.expect)(ctx.manager.isRunning()).toBe(false);
    });
    (0, vitest_1.it)('should handle stopAutomation when not started', () => {
        // Should not throw
        ctx.manager.stopAutomation();
        (0, vitest_1.expect)(ctx.manager.isRunning()).toBe(false);
    });
    (0, vitest_1.it)('cron callback should run content cycle for active characters only', async () => {
        ctx.manager.setAutomationStatus('char-1', 'active');
        // char-2 is inactive by default
        ctx.manager.startAutomation();
        // Execute the cron callback
        const cronCallback = mockCronTask._callback;
        await cronCallback();
        // Only char-1 (active status + active automation) should get content cycle
        (0, vitest_1.expect)(ctx.imgEngine.generateImage).toHaveBeenCalledTimes(1);
        (0, vitest_1.expect)(ctx.imgEngine.generateImage).toHaveBeenCalledWith('char-1', vitest_1.expect.any(String));
    });
});
//# sourceMappingURL=index.test.js.map