const express = require('express');
const path = require('path');
const archiver = require('archiver');
const fs = require('fs');
const multer = require('multer');
const AdmZip = require('adm-zip');
const { TelegramClient, Api } = require('telegram');
const { StringSession } = require('telegram/sessions');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());

const UPLOAD_DIR = path.join(__dirname, 'uploads_temp');
if (!fs.existsSync(UPLOAD_DIR)) fs.mkdirSync(UPLOAD_DIR);

const SESSIONS_DIR = path.join(__dirname, 'tg_sessions');
if (!fs.existsSync(SESSIONS_DIR)) fs.mkdirSync(SESSIONS_DIR);

const storage = multer.diskStorage({
    destination: (req, file, cb) => cb(null, UPLOAD_DIR),
    filename: (req, file, cb) => cb(null, 'restore.zip')
});
const upload = multer({ storage });

// Хранилище активных сессий в оперативной памяти
// Структура: sid -> { tgClient, phone, phoneCodeHash, apiId, apiHash }
const activeClients = {}; 

const randomDelay = (min, max) => new Promise(resolve => setTimeout(resolve, Math.floor(Math.random() * (max - min + 1)) + min));

// Вспомогательная функция: Достает или поднимает клиента из файла по sid
async function getClient(sid) {
    if (!sid) return null;
    
    // Если клиент уже в памяти и жив
    if (activeClients[sid] && activeClients[sid].tgClient && activeClients[sid].tgClient.connected) {
        return activeClients[sid].tgClient;
    }
    
    // Если клиента в памяти нет, но есть файл
    const sessionPath = path.join(SESSIONS_DIR, `${sid}.json`);
    if (fs.existsSync(sessionPath)) {
        try {
            const data = JSON.parse(fs.readFileSync(sessionPath, 'utf8'));
            const stringSession = new StringSession(data.session);
            const client = new TelegramClient(stringSession, parseInt(data.apiId), data.apiHash, { 
                connectionRetries: 3, timeout: 10000 
            });
            client.setLogLevel('none');
            client.floodSleepThreshold = 0;
            
            await client.connect();
            
            if (!activeClients[sid]) activeClients[sid] = {};
            activeClients[sid].tgClient = client;
            activeClients[sid].apiId = data.apiId;
            activeClients[sid].apiHash = data.apiHash;
            
            return client;
        } catch (err) {
            console.error(`[Telegram] Ошибка поднятия сессии ${sid}:`, err.message);
            return null;
        }
    }
    return null;
}

// Вспомогательная функция: Сохраняет текущую сессию на диск
function saveSession(sid) {
    const state = activeClients[sid];
    if (state && state.tgClient && state.tgClient.session) {
        const data = {
            apiId: state.apiId,
            apiHash: state.apiHash,
            session: state.tgClient.session.save()
        };
        fs.writeFileSync(path.join(SESSIONS_DIR, `${sid}.json`), JSON.stringify(data));
        console.log(`[Telegram] Сессия ${sid} надежно сохранена.`);
    }
}

// --- BACKUP / RESTORE ---
app.get('/backup', (req, res) => {
    res.attachment('destiny_backup.zip'); 
    const archive = archiver('zip', { zlib: { level: 9 } });
    archive.pipe(res);
    archive.directory('public/', 'public');
    if (fs.existsSync('utils')) archive.directory('utils/', 'utils');
    archive.file('server.js', { name: 'server.js' });
    archive.file('package.json', { name: 'package.json' });
    archive.finalize();
});

app.post('/restore', upload.single('backupFile'), (req, res) => {
    if (!req.file) return res.status(400).json({ success: false, error: 'No file' });
    const zipPath = req.file.path;
    const tempExtractPath = path.join(__dirname, 'temp_restore_zone');
    try {
        const zip = new AdmZip(zipPath);
        if (fs.existsSync(tempExtractPath)) fs.rmSync(tempExtractPath, { recursive: true, force: true });
        fs.mkdirSync(tempExtractPath);
        zip.extractAllTo(tempExtractPath, true);
        if (!fs.existsSync(path.join(tempExtractPath, 'public', 'index.html'))) throw new Error('Архив поврежден');
        
        if (fs.existsSync(path.join(__dirname, 'public'))) fs.rmSync(path.join(__dirname, 'public'), { recursive: true, force: true });
        if (fs.existsSync(path.join(__dirname, 'utils'))) fs.rmSync(path.join(__dirname, 'utils'), { recursive: true, force: true });
        fs.renameSync(path.join(tempExtractPath, 'public'), path.join(__dirname, 'public'));
        if (fs.existsSync(path.join(tempExtractPath, 'utils'))) fs.renameSync(path.join(tempExtractPath, 'utils'), path.join(__dirname, 'utils'));
        fs.rmSync(tempExtractPath, { recursive: true, force: true });
        fs.unlinkSync(zipPath);
        res.json({ success: true });
    } catch (err) {
        if (fs.existsSync(tempExtractPath)) fs.rmSync(tempExtractPath, { recursive: true, force: true });
        if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
        res.status(500).json({ success: false, error: err.message });
    }
});

// --- СТРАНИЦА TG ---
app.get('/tg', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'tg.html'));
});

// --- API TELEGRAM MULTIUSER ---
app.get('/api/tg/check', (req, res) => {
    const sid = req.query.sid;
    if (!sid) return res.json({ active: false });
    
    const sessionPath = path.join(SESSIONS_DIR, `${sid}.json`);
    if (fs.existsSync(sessionPath)) {
        res.json({ active: true });
    } else {
        res.json({ active: false });
    }
});

app.post('/api/tg/send-code', async (req, res) => {
    const { sid, apiId, apiHash, phone } = req.body;
    if (!sid) return res.status(400).json({ success: false, error: 'Нет SID' });
    
    console.log(`[Telegram] Старт авторизации [${sid}] номер ${phone}...`);
    try {
        if (!activeClients[sid]) activeClients[sid] = {};
        activeClients[sid].apiId = apiId;
        activeClients[sid].apiHash = apiHash;
        activeClients[sid].phone = phone;
        
        const client = new TelegramClient(new StringSession(''), parseInt(apiId), apiHash, { 
            connectionRetries: 1, timeout: 10000 
        });
        client.setLogLevel('none');
        client.floodSleepThreshold = 0; 
        
        await client.connect();
        activeClients[sid].tgClient = client;
        
        const result = await client.invoke(new Api.auth.SendCode({
            phoneNumber: phone,
            apiId: parseInt(apiId),
            apiHash: apiHash,
            settings: new Api.CodeSettings({ allowFlashcall: false, currentNumber: false, allowAppHash: false })
        }));
        
        activeClients[sid].phoneCodeHash = result.phoneCodeHash;
        res.json({ success: true, codeType: result.type.className });
    } catch (err) {
        console.error(`[Telegram] ОШИБКА SEND_CODE [${sid}]:`, err.message);
        res.status(500).json({ success: false, error: err.message });
    }
});

app.post('/api/tg/login', async (req, res) => {
    const { sid, code } = req.body;
    try {
        const state = activeClients[sid];
        if (!state || !state.tgClient) throw new Error('Серия запросов прервана. Начни заново.');
        
        await state.tgClient.invoke(new Api.auth.SignIn({ 
            phoneNumber: state.phone, 
            phoneCodeHash: state.phoneCodeHash, 
            phoneCode: code 
        }));
        saveSession(sid);
        res.json({ success: true });
    } catch (err) {
        if (err.message.includes('SESSION_PASSWORD_NEEDED')) return res.json({ success: false, requiresPassword: true });
        if (err.message.includes('PHONE_CODE_INVALID')) return res.status(400).json({ success: false, error: 'Неверный код' });
        res.status(500).json({ success: false, error: err.message });
    }
});

app.post('/api/tg/password', async (req, res) => {
    const { sid, password } = req.body;
    try {
        const state = activeClients[sid];
        if (!state || !state.tgClient) throw new Error('Серия запросов прервана.');
        
        await state.tgClient.signInWithPassword(
            { apiId: parseInt(state.apiId), apiHash: state.apiHash },
            { password: async () => password, onError: () => true }
        );
        saveSession(sid);
        res.json({ success: true });
    } catch (err) {
        res.status(500).json({ success: false, error: 'Неверный облачный пароль.' });
    }
});

app.get('/api/tg/chats', async (req, res) => {
    const sid = req.query.sid;
    try {
        const client = await getClient(sid);
        if (!client) return res.status(401).json({ success: false, error: 'Нет подключения. Авторизуйся.' });

        const dialogs = await client.getDialogs({ limit: null });
        const chatList = dialogs.map(d => ({
            id: d.id.toString(),
            name: d.title || d.name || 'Без названия'
        }));
        res.json({ success: true, chats: chatList });
    } catch (err) {
        res.status(500).json({ success: false, error: err.message });
    }
});

app.get('/api/tg/export/:id', async (req, res) => {
    const sid = req.query.sid;
    const client = await getClient(sid);
    if (!client) return res.status(401).send('Нет подключения');
    
    const chatId = req.params.id;
    try {
        const entity = await client.getEntity(chatId);
        const chatNameRaw = entity.title || entity.firstName || entity.username || 'chat';
        const chatName = chatNameRaw.replace(/[^a-zа-я0-9]/gi, '_');

        res.setHeader('Content-Type', 'application/zip');
        res.setHeader('Content-Disposition', `attachment; filename="${chatName}_export.zip"`);

        const archive = archiver('zip', { zlib: { level: 9 } });
        archive.pipe(res);

        const MAX_SIZE = 500 * 1024;
        let lineBuffer = '';
        let currentSize = 0;
        let part = 1;
        const messageCache = new Map();

        for await (const msg of client.iterMessages(entity, { reverse: true })) {
            await randomDelay(50, 250);
            
            const timeStr = new Date(msg.date * 1000).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
            let sender = 'Неизвестный';
            if (msg.sender) {
                sender = msg.sender.firstName || msg.sender.title || msg.sender.username || 'Без имени';
            } else if (entity.className === 'Channel' && !entity.megagroup) {
                sender = chatNameRaw;
            }

            let rawText = msg.message || '[Вложение/Стикер]';
            messageCache.set(msg.id, rawText.substring(0, 30).replace(/\n/g, ' '));
            
            let replyCtx = '';
            if (msg.replyTo && msg.replyTo.replyToMsgId) {
                const cachedText = messageCache.get(msg.replyTo.replyToMsgId);
                replyCtx = cachedText ? ` (в ответ на "${cachedText}...")` : ` (ответ на сообщение)`;
            }

            const line = `${timeStr} ${sender}${replyCtx}: ${rawText}\n`;
            const lineSize = Buffer.byteLength(line, 'utf8');

            if (currentSize + lineSize > MAX_SIZE) {
                archive.append(lineBuffer, { name: `${chatName}_part${part}.txt` });
                part++;
                lineBuffer = line;
                currentSize = lineSize;
            } else {
                lineBuffer += line;
                currentSize += lineSize;
            }
        }
        
        if (currentSize > 0) archive.append(lineBuffer, { name: `${chatName}_part${part}.txt` });
        await archive.finalize();
    } catch (err) {
        console.error(`[Telegram] Ошибка экспорта у ${sid}:`, err);
        if (!res.headersSent) res.status(500).send('Ошибка экспорта: ' + err.message);
    }
});

app.use(express.static(path.join(__dirname, 'public')));
app.use('/utils', express.static(path.join(__dirname, 'utils')));

app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
});
