import ScammerGuard from '#models/scammer_guard'
import ScammerKeyword from '#models/scammer_keyword'
import ScammerWhitelist from '#models/scammer_whitelist'
import ScammerLog from '#models/scammer_log'
import db from '@adonisjs/lucid/services/db'
import vine, { errors as vineErrors } from '@vinejs/vine'
import { DateTime } from 'luxon'
import { readFileSync, writeFileSync, unlinkSync } from 'fs'
import { tmpdir } from 'os'
import { join } from 'path'
import { randomBytes } from 'crypto'

// Simple CSV helpers (no external deps)
function toCSV(rows) {
    if (!rows.length) return ''
    const headers = Object.keys(rows[0])
    const escape = v => {
        const s = String(v ?? '')
        if (s.includes(',') || s.includes('"') || s.includes('\n')) return `"${s.replace(/"/g, '""')}"`
        return s
    }
    return [headers.join(','), ...rows.map(r => headers.map(h => escape(r[h])).join(','))].join('\n')
}

function fromCSV(text) {
    const lines = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n').filter(l => l.trim())
    if (lines.length < 2) return []
    const headers = lines[0].split(',').map(h => h.trim().replace(/^"|"$/g, ''))
    return lines.slice(1).map(line => {
        const vals = []
        let cur = '', inQ = false
        for (let i = 0; i < line.length; i++) {
            if (line[i] === '"') {
                if (inQ && line[i+1] === '"') { cur += '"'; i++ }
                else inQ = !inQ
            } else if (line[i] === ',' && !inQ) { vals.push(cur); cur = '' }
            else cur += line[i]
        }
        vals.push(cur)
        return Object.fromEntries(headers.map((h, i) => [h, (vals[i] || '').trim()]))
    })
}

// Helper: get scammer settings
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 }
}

async function setSetting(key, value) {
    const existing = await db.from('scammer_settings').where('key', key).first()
    if (existing) {
        await db.from('scammer_settings').where('key', key).update({ value, updated_at: DateTime.now().toISO() })
    } else {
        await db.from('scammer_settings').insert({ key, value, created_at: DateTime.now().toISO(), updated_at: DateTime.now().toISO() })
    }
}

export default class ScammerController {

    // ─── MAIN INDEX ─────────────────────────────────────────────────────────────

    async index({ view, auth, request }) {
        const tab = request.input('tab', 'blacklist')
        const page = parseInt(request.input('page', '1')) || 1
        const perPage = 20

        // Bug #9 fix: Single bulk UPDATE instead of N+1 loop
        const now = DateTime.now().toISO()
        await db.from('scammer_guards')
            .where('is_active', true)
            .whereNotNull('expires_at')
            .where('expires_at', '<', now)
            .update({ is_active: false, updated_at: now })

        const scammers = await ScammerGuard.query().orderBy('created_at', 'desc')

        const totalCount = scammers.length
        const activeCount = scammers.filter(s => s.isActive).length
        const inactiveCount = scammers.filter(s => !s.isActive).length
        const totalHits = scammers.reduce((a, s) => a + (parseInt(s.hitCount) || 0), 0)

        // Keywords
        const keywords = await ScammerKeyword.query().orderBy('created_at', 'desc')

        // Whitelist
        const whitelist = await ScammerWhitelist.query().orderBy('created_at', 'desc')

        // Logs (paginated)
        const logsTotal = await db.from('scammer_logs').count('id as total').first()
        const logsTotalCount = parseInt(logsTotal?.total || 0)
        const logsOffset = (page - 1) * perPage
        const logs = await db.from('scammer_logs')
            .orderBy('created_at', 'desc')
            .limit(perPage)
            .offset(logsOffset)
        const logsTotalPages = Math.ceil(logsTotalCount / perPage)

        // Settings
        const settings = {
            globalThreshold: await getSetting('global_threshold', '0'),
            autoExpireDays: await getSetting('auto_expire_days', '0'),
            sharedDbEnabled: await getSetting('shared_db_enabled', '0'),
            sharedDbUrl: await getSetting('shared_db_url', ''),
            sharedDbToken: await getSetting('shared_db_token', ''),
            thresholdAction: await getSetting('threshold_action', 'warn'),
            // AI Settings
            aiEnabled: await getSetting('ai_enabled', '0'),
            aiApiKey: await getSetting('ai_api_key', ''),
            aiAutoReplyEnabled: await getSetting('ai_auto_reply_enabled', '0'),
            aiAutoReplyPrefix: await getSetting('ai_auto_reply_prefix', ''),
            aiAutoReplySystemPrompt: await getSetting('ai_auto_reply_system_prompt', ''),
            aiGreetingEnabled: await getSetting('ai_greeting_enabled', '0'),
            aiGreetingPrompt: await getSetting('ai_greeting_prompt', ''),
            aiScamDetectEnabled: await getSetting('ai_scam_detect_enabled', '0'),
            aiScamDetectThreshold: await getSetting('ai_scam_detect_threshold', '80'),
            aiScamAction: await getSetting('ai_scam_action', 'warn'),
        }

        return view.render('scammer/index', {
            scammers,
            totalCount,
            activeCount,
            inactiveCount,
            totalHits,
            keywords,
            whitelist,
            logs,
            logsTotalCount,
            logsTotalPages,
            logsCurrentPage: page,
            settings,
            tab,
        })
    }

    // ─── BLACKLIST CRUD ──────────────────────────────────────────────────────────

    async store({ request, response, auth }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        try {
            const data = await request.validateUsing(vine.compile(vine.object({
                phone: vine.string().trim().maxLength(30),
                name: vine.string().trim().maxLength(100).optional(),
                reason: vine.string().trim().maxLength(500).optional(),
                action: vine.enum(['log', 'warn', 'kick', 'block']),
                notes: vine.string().trim().maxLength(500).optional(),
                expires_days: vine.number().min(0).max(3650).optional(),
                threshold: vine.number().min(0).max(999).optional(),
            })))

            const phone = data.phone.replace(/[\s\-\+]/g, '')
            const existing = await ScammerGuard.query().where('phone', phone).first()
            if (existing) return response.unprocessableEntity({ message: 'Nombor ini sudah ada dalam senarai hitam.' })

            let expiresAt = null
            if (data.expires_days && data.expires_days > 0) {
                expiresAt = DateTime.now().plus({ days: data.expires_days }).toISO()
            }
            // Check auto-expire setting
            if (!expiresAt) {
                const autoExpireDays = parseInt(await getSetting('auto_expire_days', '0'))
                if (autoExpireDays > 0) {
                    expiresAt = DateTime.now().plus({ days: autoExpireDays }).toISO()
                }
            }

            await ScammerGuard.create({
                phone,
                name: data.name || null,
                reason: data.reason || null,
                action: data.action,
                isActive: true,
                addedBy: auth.use('web').user?.id || null,
                hitCount: 0,
                notes: data.notes || null,
                expiresAt,
                threshold: data.threshold || 0,
            })

            return response.ok({ message: 'Nombor berjaya ditambah ke senarai hitam.' })
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'Input tidak sah. Sila semak dan cuba lagi.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    async update({ params, request, response }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const scammer = await ScammerGuard.find(params.id)
        if (!scammer) return response.notFound({ message: 'Rekod tidak dijumpai.' })
        try {
            const data = await request.validateUsing(vine.compile(vine.object({
                name: vine.string().trim().maxLength(100).optional(),
                reason: vine.string().trim().maxLength(500).optional(),
                action: vine.enum(['log', 'warn', 'kick', 'block']).optional(),
                notes: vine.string().trim().maxLength(500).optional(),
                expires_days: vine.number().min(0).max(3650).optional(),
                threshold: vine.number().min(0).max(999).optional(),
            })))
            if (data.name !== undefined) scammer.name = data.name || null
            if (data.reason !== undefined) scammer.reason = data.reason || null
            if (data.action !== undefined) scammer.action = data.action
            if (data.notes !== undefined) scammer.notes = data.notes || null
            if (data.threshold !== undefined) scammer.threshold = data.threshold || 0
            if (data.expires_days !== undefined) {
                scammer.expiresAt = data.expires_days > 0
                    ? DateTime.now().plus({ days: data.expires_days }).toISO()
                    : null
            }
            await scammer.save()
            return response.ok({ message: 'Rekod berjaya dikemaskini.' })
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'Input tidak sah. Sila semak dan cuba lagi.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    async destroy({ params, response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const scammer = await ScammerGuard.find(params.id)
        if (!scammer) return response.notFound({ message: 'Rekod tidak dijumpai.' })
        await scammer.delete()
        return response.ok({ message: 'Nombor berjaya dipadamkan dari senarai hitam.' })
    }

    async toggle({ params, response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const scammer = await ScammerGuard.find(params.id)
        if (!scammer) return response.notFound({ message: 'Rekod tidak dijumpai.' })
        scammer.isActive = !scammer.isActive
        await scammer.save()
        return response.ok({
            message: scammer.isActive ? 'Nombor diaktifkan.' : 'Nombor dinyahaktifkan.',
            isActive: scammer.isActive,
        })
    }

    // ─── EXPORT / IMPORT ────────────────────────────────────────────────────────

    async exportCsv({ response }) {
        const scammers = await ScammerGuard.query().orderBy('created_at', 'desc')
        const rows = scammers.map(s => ({
            phone: s.phone,
            name: s.name || '',
            reason: s.reason || '',
            action: s.action,
            is_active: s.isActive ? '1' : '0',
            hit_count: s.hitCount || 0,
            last_seen_at: s.lastSeenAt || '',
            expires_at: s.expiresAt || '',
            notes: s.notes || '',
            created_at: s.createdAt ? s.createdAt.toISO() : '',
        }))
        const csv = toCSV(rows)
        // Bug #4 fix: Delete temp file after download to avoid disk accumulation
        const tmpFile = join(tmpdir(), `scammer_export_${randomBytes(6).toString('hex')}.csv`)
        writeFileSync(tmpFile, csv, 'utf8')
        response.header('Content-Type', 'text/csv')
        response.header('Content-Disposition', `attachment; filename="scammer_blacklist_${DateTime.now().toFormat('yyyyMMdd_HHmm')}.csv"`)
        await response.download(tmpFile)
        try { unlinkSync(tmpFile) } catch (_) {}
    }

    async importCsv({ request, response, auth }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const file = request.file('csv_file', { size: '5mb', extnames: ['csv'] })
        if (!file) return response.badRequest({ message: 'Fail CSV diperlukan.' })
        if (file.hasErrors) return response.unprocessableEntity({ message: file.errors[0]?.message || 'Fail tidak sah.' })

        const tmpPath = join(tmpdir(), `import_${randomBytes(6).toString('hex')}.csv`)
        await file.move(tmpdir(), { name: tmpPath.split('/').pop() })

        let added = 0, skipped = 0, errors = []
        const userId = auth.use('web').user?.id || null

        try {
            const text = readFileSync(tmpPath, 'utf8')
            const records = fromCSV(text)
            for (const row of records) {
                try {
                    const phone = (row.phone || '').replace(/[\s\-\+]/g, '')
                    if (!phone) { skipped++; continue }
                    const existing = await ScammerGuard.query().where('phone', phone).first()
                    if (existing) { skipped++; continue }
                    const action = ['log', 'warn', 'kick', 'block'].includes(row.action) ? row.action : 'log'
                    await ScammerGuard.create({
                        phone,
                        name: row.name || null,
                        reason: row.reason || null,
                        action,
                        isActive: row.is_active !== '0',
                        addedBy: userId,
                        hitCount: parseInt(row.hit_count) || 0,
                        lastSeenAt: row.last_seen_at || null,
                        expiresAt: row.expires_at || null,
                        notes: row.notes || null,
                        threshold: parseInt(row.threshold) || 0,
                    })
                    added++
                } catch (e) { errors.push(e.message) }
            }
            try { unlinkSync(tmpPath) } catch (_) {}
        } catch (e) {
            return response.internalServerError({ message: `Gagal baca CSV: ${e.message}` })
        }

        return response.ok({ message: `Import selesai: ${added} ditambah, ${skipped} dilangkau.`, added, skipped, errors })
    }

    // ─── KEYWORDS ───────────────────────────────────────────────────────────────

    async storeKeyword({ request, response, auth }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        try {
            const data = await request.validateUsing(vine.compile(vine.object({
                keyword: vine.string().trim().maxLength(200),
                action: vine.enum(['log', 'warn', 'kick', 'block']),
                notes: vine.string().trim().maxLength(500).optional(),
            })))
            const existing = await ScammerKeyword.query().where('keyword', data.keyword).first()
            if (existing) return response.unprocessableEntity({ message: 'Keyword ini sudah ada.' })
            await ScammerKeyword.create({
                keyword: data.keyword,
                action: data.action,
                notes: data.notes || null,
                isActive: true,
                hitCount: 0,
                addedBy: auth.use('web').user?.id || null,
            })
            return response.ok({ message: 'Keyword berjaya ditambah.' })
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'Input tidak sah. Sila semak dan cuba lagi.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    async destroyKeyword({ params, response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const kw = await ScammerKeyword.find(params.id)
        if (!kw) return response.notFound({ message: 'Keyword tidak dijumpai.' })
        await kw.delete()
        return response.ok({ message: 'Keyword berjaya dipadamkan.' })
    }

    async toggleKeyword({ params, response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const kw = await ScammerKeyword.find(params.id)
        if (!kw) return response.notFound({ message: 'Keyword tidak dijumpai.' })
        kw.isActive = !kw.isActive
        await kw.save()
        return response.ok({ message: kw.isActive ? 'Keyword diaktifkan.' : 'Keyword dinyahaktifkan.', isActive: kw.isActive })
    }

    // ─── WHITELIST ───────────────────────────────────────────────────────────────

    async storeWhitelist({ request, response, auth }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        try {
            const data = await request.validateUsing(vine.compile(vine.object({
                phone: vine.string().trim().maxLength(30),
                name: vine.string().trim().maxLength(100).optional(),
                reason: vine.string().trim().maxLength(500).optional(),
            })))
            const phone = data.phone.replace(/[\s\-\+]/g, '')
            const existing = await ScammerWhitelist.query().where('phone', phone).first()
            if (existing) return response.unprocessableEntity({ message: 'Nombor ini sudah ada dalam whitelist.' })
            await ScammerWhitelist.create({
                phone,
                name: data.name || null,
                reason: data.reason || null,
                addedBy: auth.use('web').user?.id || null,
            })
            return response.ok({ message: 'Nombor berjaya ditambah ke whitelist.' })
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'Input tidak sah. Sila semak dan cuba lagi.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    async destroyWhitelist({ params, response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const entry = await ScammerWhitelist.find(params.id)
        if (!entry) return response.notFound({ message: 'Rekod tidak dijumpai.' })
        await entry.delete()
        return response.ok({ message: 'Nombor berjaya dipadamkan dari whitelist.' })
    }

    // ─── LOGS ────────────────────────────────────────────────────────────────────

    async clearLogs({ response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        await db.from('scammer_logs').delete()
        return response.ok({ message: 'Semua log telah dipadamkan.' })
    }

    async exportLogs({ response }) {
        const logs = await db.from('scammer_logs').orderBy('created_at', 'desc')
        const csv = toCSV(logs)
        // Bug #4 fix: Delete temp file after download to avoid disk accumulation
        const tmpFile = join(tmpdir(), `scammer_logs_${randomBytes(6).toString('hex')}.csv`)
        writeFileSync(tmpFile, csv, 'utf8')
        response.header('Content-Type', 'text/csv')
        response.header('Content-Disposition', `attachment; filename="scammer_logs_${DateTime.now().toFormat('yyyyMMdd_HHmm')}.csv"`)
        await response.download(tmpFile)
        try { unlinkSync(tmpFile) } catch (_) {}
    }

    // ─── SETTINGS ────────────────────────────────────────────────────────────────

    async saveSettings({ request, response }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        try {
            const data = await request.validateUsing(vine.compile(vine.object({
                global_threshold: vine.string().trim().optional(),
                auto_expire_days: vine.string().trim().optional(),
                shared_db_enabled: vine.string().trim().optional(),
                shared_db_url: vine.string().trim().maxLength(500).nullable().optional(),
                shared_db_token: vine.string().trim().maxLength(500).nullable().optional(),
                threshold_action: vine.enum(['log', 'warn', 'kick', 'block']).optional(),
                // AI Settings
                ai_enabled: vine.string().trim().optional(),
                ai_api_key: vine.string().trim().maxLength(200).nullable().optional(),
                ai_auto_reply_enabled: vine.string().trim().optional(),
                ai_auto_reply_prefix: vine.string().trim().maxLength(50).nullable().optional(),
                ai_auto_reply_system_prompt: vine.string().trim().maxLength(2000).nullable().optional(),
                ai_greeting_enabled: vine.string().trim().optional(),
                ai_greeting_prompt: vine.string().trim().maxLength(2000).nullable().optional(),
                ai_scam_detect_enabled: vine.string().trim().optional(),
                ai_scam_detect_threshold: vine.string().trim().optional(),
                ai_scam_action: vine.enum(['log', 'warn', 'kick', 'block']).optional(),
            })))
            for (const [key, value] of Object.entries(data)) {
                if (value !== undefined) await setSetting(key, value === null ? '' : value)
            }
            return response.ok({ message: 'Tetapan berjaya disimpan.' })
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'Input tidak sah. Sila semak dan cuba lagi.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    // ─── AI KEY TEST ─────────────────────────────────────────────────────────────

    async testAiKey({ request, response }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        try {
            const { apiKey } = await request.validateUsing(vine.compile(vine.object({
                apiKey: vine.string().trim().minLength(10),
            })))
            const { testApiKey } = await import('#services/deepseek_service')
            const result = await testApiKey(apiKey)
            if (result.valid) {
                return response.ok({ message: result.message })
            } else {
                return response.badRequest({ message: result.message })
            }
        } catch (error) {
            if (error instanceof vineErrors.E_VALIDATION_ERROR) return response.unprocessableEntity({ message: 'API key diperlukan.', errors: error.messages })
            return response.internalServerError({ message: error.message })
        }
    }

    // ─── SHARED DB SYNC ──────────────────────────────────────────────────────────

    async syncSharedDb({ response, request }) {
        if (!request.ajax()) return response.badRequest('Invalid request.')
        const enabled = await getSetting('shared_db_enabled', '0')
        if (enabled !== '1') return response.badRequest({ message: 'Shared DB tidak diaktifkan.' })
        const sharedUrl = await getSetting('shared_db_url', '')
        const sharedToken = await getSetting('shared_db_token', '')
        if (!sharedUrl) return response.badRequest({ message: 'URL Shared DB tidak dikonfigurasi.' })

        try {
            const { default: axios } = await import('axios')
            const res = await axios.get(`${sharedUrl}/api/scammer-guard`, {
                headers: sharedToken ? { Authorization: `Bearer ${sharedToken}` } : {},
                timeout: 10000,
            })
            const remoteList = res.data?.data || []
            let added = 0, skipped = 0
            for (const entry of remoteList) {
                const phone = String(entry.phone || '').replace(/[\s\-\+]/g, '')
                if (!phone) { skipped++; continue }
                const existing = await ScammerGuard.query().where('phone', phone).first()
                if (existing) { skipped++; continue }
                await ScammerGuard.create({
                    phone,
                    name: entry.name || null,
                    reason: entry.reason || '[Shared DB]',
                    action: ['log', 'warn', 'kick', 'block'].includes(entry.action) ? entry.action : 'warn',
                    isActive: true,
                    hitCount: 0,
                    notes: 'Disync dari Shared DB',
                })
                added++
            }
            return response.ok({ message: `Sync selesai: ${added} ditambah, ${skipped} dilangkau.`, added, skipped })
        } catch (e) {
            return response.internalServerError({ message: `Sync gagal: ${e.message}` })
        }
    }
}
