import consola from 'consola';
import serialize from '#wa/utils/serialize';
import { DateTime } from 'luxon';
import axios from 'axios';
import db from '@adonisjs/lucid/services/db';
import { detectScam, generateAutoReply } from '#services/deepseek_service';

const WEBHOOK_TIMEOUT = 5000;

// ─── HELPERS ────────────────────────────────────────────────────────────────

async function getSetting(key, fallback = null) {
    try {
        const row = await db.from('scammer_settings').where('key', key).first();
        return row ? row.value : fallback;
    } catch { return fallback; }
}

function extractText(m) {
    try {
        const msg = m.message || {};
        return (
            msg.conversation ||
            msg.extendedTextMessage?.text ||
            msg.imageMessage?.caption ||
            msg.videoMessage?.caption ||
            msg.documentMessage?.caption ||
            ''
        );
    } catch { return ''; }
}

// ─── SCAMMER GUARD ───────────────────────────────────────────────────────────

/**
 * Check blacklist — phone normalized string
 * Returns record or null. Also checks expiry.
 */
async function checkScammer(phone) {
    try {
        const normalized = String(phone).replace(/[\s\-\+]/g, '');
        if (!normalized) return null;
        const now = DateTime.now().toISO();
        const record = await db.from('scammer_guards')
            .where('phone', normalized)
            .where('is_active', true)
            .first();
        if (!record) return null;
        // Check expiry
        if (record.expires_at && record.expires_at < now) {
            await db.from('scammer_guards').where('id', record.id).update({ is_active: false });
            return null;
        }
        return record;
    } catch (e) {
        consola.warn('[ScammerGuard] DB check failed:', e.message);
        return null;
    }
}

/**
 * Check whitelist — skip if whitelisted
 */
async function isWhitelisted(phone) {
    try {
        const normalized = String(phone).replace(/[\s\-\+]/g, '');
        const row = await db.from('scammer_whitelist').where('phone', normalized).first();
        return !!row;
    } catch { return false; }
}

/**
 * Check active keywords against message text
 * Returns first matching keyword record or null
 */
async function checkKeywords(text) {
    try {
        if (!text) return null;
        const keywords = await db.from('scammer_keywords').where('is_active', true);
        const lower = text.toLowerCase();
        for (const kw of keywords) {
            if (lower.includes(kw.keyword.toLowerCase())) {
                return kw;
            }
        }
        return null;
    } catch (e) {
        consola.warn('[ScammerGuard] Keyword check failed:', e.message);
        return null;
    }
}

/**
 * Threshold tracking — count hits per phone for keyword matches
 * Returns true if threshold exceeded and auto-blacklisted
 */
async function checkThreshold(phone, messageText) {
    try {
        const globalThreshold = parseInt(await getSetting('global_threshold', '0'));
        if (!globalThreshold || globalThreshold <= 0) return false;

        const normalized = String(phone).replace(/[\s\-\+]/g, '');
        // Count keyword-triggered logs for this phone
        const countRow = await db.from('scammer_logs')
            .where('phone', normalized)
            .where('triggered_by', 'keyword')
            .count('id as total')
            .first();
        const count = parseInt(countRow?.total || 0);

        if (count >= globalThreshold) {
            // Check not already blacklisted
            const existing = await db.from('scammer_guards').where('phone', normalized).first();
            if (!existing) {
                const thresholdAction = await getSetting('threshold_action', 'warn');
                await db.from('scammer_guards').insert({
                    phone: normalized,
                    name: null,
                    reason: `Auto-blacklisted: melebihi threshold ${globalThreshold} hit keyword`,
                    action: thresholdAction,
                    is_active: true,
                    hit_count: count,
                    last_seen_at: DateTime.now().toISO(),
                    created_at: DateTime.now().toISO(),
                    updated_at: DateTime.now().toISO(),
                });
                consola.warn(`[ScammerGuard] Auto-blacklisted ${normalized} (threshold ${globalThreshold})`);
                return true;
            }
        }
        return false;
    } catch (e) {
        consola.warn('[ScammerGuard] Threshold check failed:', e.message);
        return false;
    }
}

/**
 * Update hit count and last seen for scammer record
 */
async function updateScammerHit(id) {
    try {
        await db.from('scammer_guards')
            .where('id', id)
            .update({
                hit_count: db.raw('hit_count + 1'),
                last_seen_at: DateTime.now().toISO(),
                updated_at: DateTime.now().toISO(),
            });
    } catch (e) {
        consola.warn('[ScammerGuard] Failed to update hit count:', e.message);
    }
}

/**
 * Write to scammer_logs
 * triggered_by valid enum values: 'blacklist', 'keyword', 'threshold'
 * For other values (e.g. 'ai_detect'), we map to 'blacklist' and prepend the
 * actual trigger type in matched_keyword to preserve information.
 */
const VALID_TRIGGERED_BY = ['blacklist', 'keyword', 'threshold'];
const VALID_ACTION_TAKEN  = ['log', 'warn', 'kick', 'block', 'threshold_blacklisted'];

async function writeLog({ phone, name, groupJid, groupName, deviceId, actionTaken, triggeredBy, matchedKeyword, messagePreview }) {
    try {
        // Normalize triggered_by — if not in enum, prefix info into matched_keyword
        let safeTriggeredBy = triggeredBy || 'blacklist';
        let safeMatchedKeyword = matchedKeyword || null;
        if (!VALID_TRIGGERED_BY.includes(safeTriggeredBy)) {
            // Preserve original trigger type as a prefix in matched_keyword
            safeMatchedKeyword = safeTriggeredBy + (safeMatchedKeyword ? ': ' + safeMatchedKeyword : '');
            safeTriggeredBy = 'blacklist'; // fallback to valid enum value
        }

        // Normalize action_taken
        let safeActionTaken = actionTaken || 'log';
        if (!VALID_ACTION_TAKEN.includes(safeActionTaken)) {
            safeActionTaken = 'log';
        }

        await db.from('scammer_logs').insert({
            phone: phone || '',
            name: name || null,
            group_jid: groupJid || null,
            group_name: groupName || null,
            device_id: deviceId || null,
            action_taken: safeActionTaken,
            triggered_by: safeTriggeredBy,
            matched_keyword: safeMatchedKeyword ? String(safeMatchedKeyword).substring(0, 200) : null,
            message_preview: messagePreview ? String(messagePreview).substring(0, 300) : null,
            created_at: DateTime.now().toISO(),
            updated_at: DateTime.now().toISO(),
        });
    } catch (e) {
        consola.warn('[ScammerGuard] Failed to write log:', e.message);
    }
}

/**
 * Sync to shared DB (fire & forget)
 */
async function syncToSharedDb(phone, name, reason) {
    try {
        const enabled = await getSetting('shared_db_enabled', '0');
        if (enabled !== '1') return;
        const sharedUrl = await getSetting('shared_db_url', '');
        const sharedToken = await getSetting('shared_db_token', '');
        if (!sharedUrl) return;
        await axios.post(`${sharedUrl}/api/scammer-guard`, {
            phone, name: name || null, reason: reason || 'Detected by ScammerGuard', action: 'warn',
        }, {
            headers: sharedToken ? { Authorization: `Bearer ${sharedToken}` } : {},
            timeout: 5000,
        });
    } catch { /* silent */ }
}

/**
 * Execute scammer action on the WhatsApp socket
 * action: log | warn | kick | block
 */
async function executeScammerAction({ sock, m, scammer, deviceId, triggeredBy, matchedKeyword }) {
    const senderJid = m.sender?.pn || m.sender?.id || '';
    const chatJid = m.key?.remoteJid || m.chat;
    const isGroup = chatJid?.endsWith('@g.us');
    const action = scammer.action;
    const phone = scammer.phone;
    const senderName = m.sender?.pushName || m.sender?.name || null;
    const msgText = extractText(m);

    consola.warn(`[ScammerGuard: ${deviceId}] Detected ${phone} | trigger: ${triggeredBy} | action: ${action} | chat: ${chatJid}`);

    // Update hit count
    if (scammer.id) await updateScammerHit(scammer.id);

    // Write log
    await writeLog({
        phone,
        name: senderName,
        groupJid: isGroup ? chatJid : null,
        groupName: null,
        deviceId,
        actionTaken: action,
        triggeredBy: triggeredBy || 'blacklist',
        matchedKeyword: matchedKeyword || null,
        messagePreview: msgText,
    });

    // Sync to shared DB (async, non-blocking)
    syncToSharedDb(phone, senderName, scammer.reason).catch(() => {});

    if (action === 'log') {
        consola.info(`[ScammerGuard: ${deviceId}] Logged message from ${phone}`);
        return;
    }

    if (action === 'warn') {
        try {
            const warningText = `⚠️ *ScammerGuard Alert*\n\nNombor *+${phone}* telah dikesan dalam senarai hitam scammer.\n${scammer.reason ? `Sebab: ${scammer.reason}` : ''}\n\nSila berhati-hati.`;
            await sock.sendMessage(chatJid, { text: warningText });
        } catch (e) {
            consola.warn(`[ScammerGuard: ${deviceId}] Warn failed: ${e.message}`);
        }
        return;
    }

    if (action === 'kick') {
        if (!isGroup) { consola.warn(`[ScammerGuard: ${deviceId}] Kick skipped — not a group`); return; }
        if (!senderJid) { consola.warn(`[ScammerGuard: ${deviceId}] Kick failed — no senderJid`); return; }
        try {
            await sock.groupParticipantsUpdate(chatJid, [senderJid], 'remove');
            consola.info(`[ScammerGuard: ${deviceId}] Kicked ${senderJid} from ${chatJid}`);
            try {
                await sock.sendMessage(chatJid, { text: `🚫 *ScammerGuard*: Ahli *+${phone}* telah dikeluarkan kerana dikesan sebagai scammer.` });
            } catch (_) {}
        } catch (e) {
            consola.warn(`[ScammerGuard: ${deviceId}] Kick failed: ${e.message}`);
        }
        return;
    }

    if (action === 'block') {
        if (!senderJid) { consola.warn(`[ScammerGuard: ${deviceId}] Block failed — no senderJid`); return; }
        try {
            await sock.updateBlockStatus(senderJid, 'block');
            consola.info(`[ScammerGuard: ${deviceId}] Blocked ${senderJid}`);
        } catch (e) {
            consola.warn(`[ScammerGuard: ${deviceId}] Block failed: ${e.message}`);
        }
        return;
    }
}

// ─── MAIN EVENT HANDLER ──────────────────────────────────────────────────────

export default async function ({ id, sock, event }) {
    if (event.type !== 'notify') return;

    for (const message of event.messages) {
        const startTime = Date.now();
        if (!message?.message) {
            consola.info(message.messageStubParameters);
            continue;
        }
        const m = await serialize({ id, sock, WAMessage: message });
        if (!m) continue;

        // Skip own messages
        if (m.fromMe || m.key?.fromMe) continue;

        // Extract sender phone
        const senderPnJid = m.sender?.pn || m.sender?.id || '';
        const senderPhone = senderPnJid.replace(/@.*$/, '');

        if (senderPhone) {
            // ── 1. Whitelist check (skip all checks if whitelisted) ──────────
            const whitelisted = await isWhitelisted(senderPhone);

            if (!whitelisted) {
                // ── 2. Blacklist check ────────────────────────────────────────
                const scammer = await checkScammer(senderPhone);
                if (scammer) {
                    await executeScammerAction({ sock, m, scammer, deviceId: id, triggeredBy: 'blacklist' });
                    if (scammer.action === 'block') continue;
                } else {
                    // ── 3. Keyword check ─────────────────────────────────────
                    const msgText = extractText(m);
                    const matchedKw = await checkKeywords(msgText);
                    if (matchedKw) {
                        consola.warn(`[ScammerGuard: ${id}] Keyword match: "${matchedKw.keyword}" from ${senderPhone}`);

                        // Update keyword hit count
                        await db.from('scammer_keywords').where('id', matchedKw.id)
                            .update({ hit_count: db.raw('hit_count + 1'), updated_at: DateTime.now().toISO() });

                        // Execute keyword action — writeLog is handled inside executeScammerAction
                        await executeScammerAction({
                            sock, m,
                            scammer: { phone: senderPhone, action: matchedKw.action, reason: `Keyword: ${matchedKw.keyword}`, id: null },
                            deviceId: id,
                            triggeredBy: 'keyword',
                            matchedKeyword: matchedKw.keyword,
                        });

                        // ── 4. Threshold check ───────────────────────────────
                        const autoBlacklisted = await checkThreshold(senderPhone, msgText);
                        if (autoBlacklisted) {
                            await writeLog({
                                phone: senderPhone,
                                name: m.sender?.pushName || null,
                                deviceId: id,
                                actionTaken: 'threshold_blacklisted',
                                triggeredBy: 'threshold',
                                messagePreview: msgText,
                            });
                        }

                        if (matchedKw.action === 'block') continue;
                    }

                    // ── 5. AI Scam Detection ─────────────────────────────
                    try {
                        const aiEnabled = await getSetting('ai_enabled', '0');
                        const aiScamEnabled = await getSetting('ai_scam_detect_enabled', '0');
                        if (aiEnabled === '1' && aiScamEnabled === '1') {
                            const aiApiKey = await getSetting('ai_api_key', '');
                            const aiMsgText = msgText || extractText(m);
                            if (aiApiKey && aiMsgText && aiMsgText.length > 10) {
                                const aiThreshold = parseInt(await getSetting('ai_scam_detect_threshold', '80') || '80');
                                const aiResult = await detectScam(aiApiKey, aiMsgText);
                                if (aiResult.isScam && aiResult.confidence >= aiThreshold) {
                                    consola.warn(`[AI Scam: ${id}] ⚠️ Detected ${senderPhone} conf:${aiResult.confidence}% — ${aiResult.reason}`);
                                    const aiScamAction = await getSetting('ai_scam_action', 'warn');
                                    const aiChatJid = m.key?.remoteJid || m.chat;
                                    await writeLog({
                                        phone: senderPhone,
                                        name: m.sender?.pushName || null,
                                        groupJid: aiChatJid?.endsWith('@g.us') ? aiChatJid : null,
                                        deviceId: id,
                                        actionTaken: aiScamAction,
                                        triggeredBy: 'ai_detect',
                                        matchedKeyword: `AI (${aiResult.confidence}%): ${aiResult.reason}`,
                                        messagePreview: aiMsgText,
                                    });
                                    await executeScammerAction({
                                        sock, m,
                                        scammer: {
                                            phone: senderPhone,
                                            action: aiScamAction,
                                            reason: `AI Detect (${aiResult.confidence}%): ${aiResult.reason}`,
                                            id: null,
                                        },
                                        deviceId: id,
                                        triggeredBy: 'ai_detect',
                                    });
                                    if (aiScamAction === 'block') continue;
                                }
                            }
                        }
                    } catch (aiErr) {
                        consola.warn(`[AI Scam: ${id}] Error:`, aiErr.message);
                    }
                }
            }
        }

        // ── AI Auto-Reply ─────────────────────────────────────────────────────
        try {
            const aiEnabled = await getSetting('ai_enabled', '0');
            const aiAutoReplyEnabled = await getSetting('ai_auto_reply_enabled', '0');
            if (aiEnabled === '1' && aiAutoReplyEnabled === '1') {
                const aiMsgText = extractText(m);
                if (aiMsgText) {
                    const prefix = (await getSetting('ai_auto_reply_prefix', '') || '').trim().toLowerCase();
                    const shouldReply = prefix
                        ? aiMsgText.toLowerCase().startsWith(prefix)
                        : true;
                    if (shouldReply) {
                        const queryText = prefix
                            ? aiMsgText.substring(prefix.length).trim()
                            : aiMsgText;
                        if (queryText) {
                            const aiApiKey = await getSetting('ai_api_key', '');
                            if (aiApiKey) {
                                const sysPrompt = await getSetting('ai_auto_reply_system_prompt', '') || '';
                                const aiReply = await generateAutoReply(aiApiKey, queryText, sysPrompt);
                                if (aiReply) {
                                    const chatJid = m.key?.remoteJid || m.chat;
                                    await sock.sendMessage(chatJid, { text: aiReply });
                                    consola.info(`[AI Reply: ${id}] ✅ Replied in ${chatJid}`);
                                }
                            }
                        }
                    }
                }
            }
        } catch (aiReplyErr) {
            consola.warn(`[AI Reply: ${id}] Error:`, aiReplyErr.message);
        }

        // ── Webhook processing ────────────────────────────────────────────────
        if (m.webhook) {
            try {
                await Promise.all([
                    sock.readMessages([m.key]),
                    axios.post(m.webhook, m, { timeout: WEBHOOK_TIMEOUT }),
                ]);
                const duration = Date.now() - startTime;
                consola.success(`[WEBHOOK: ${id}] Successfully processed in ${duration}ms`);
            } catch (error) {
                let errorMessage = `[WEBHOOK: ${id}] Something went wrong.`;
                if (axios.isAxiosError(error)) {
                    if (error.response) {
                        errorMessage = `[WEBHOOK: ${id}] ${error.response.status} ${error.response.statusText}`;
                    } else if (error.code === 'ECONNABORTED') {
                        errorMessage = `[WEBHOOK: ${id}] Request timed out after ${WEBHOOK_TIMEOUT}.`;
                    } else {
                        errorMessage = `[WEBHOOK: ${id}] ${error.message}`;
                    }
                } else {
                    errorMessage = `[WEBHOOK: ${id}] ${error.message}`;
                }
                consola.error(errorMessage);
            }
        }
    }
}
