const { getDatabase } = require('../config/database');

// ── Patch Groups ──────────────────────────────────────────────────────────────

const getGroups = (req, res) => {
    const db = getDatabase();
    const groups = db.prepare('SELECT * FROM patch_groups ORDER BY sort_order, name').all();
    const policies = db.prepare('SELECT * FROM patch_policies ORDER BY group_id').all();
    const agentCounts = db.prepare(`
        SELECT group_id, COUNT(*) as count FROM patch_agent_groups GROUP BY group_id
    `).all();

    res.json(groups.map(g => ({
        ...g,
        policies: policies.filter(p => p.group_id === g.id),
        agent_count: agentCounts.find(a => a.group_id === g.id)?.count || 0,
    })));
};

const createGroup = (req, res) => {
    const { name, description, color = '#3B82F6' } = req.body;
    if (!name?.trim()) return res.status(400).json({ error: 'Name erforderlich' });
    const db = getDatabase();
    const maxOrder = db.prepare('SELECT MAX(sort_order) as m FROM patch_groups').get();
    try {
        const result = db.prepare(
            'INSERT INTO patch_groups (name, description, color, sort_order) VALUES (?, ?, ?, ?)'
        ).run(name.trim(), description || null, color, (maxOrder.m || 0) + 1);
        res.status(201).json(db.prepare('SELECT * FROM patch_groups WHERE id = ?').get(result.lastInsertRowid));
    } catch (e) {
        if (e.message.includes('UNIQUE')) return res.status(409).json({ error: 'Gruppe existiert bereits' });
        throw e;
    }
};

const updateGroup = (req, res) => {
    const db = getDatabase();
    const existing = db.prepare('SELECT * FROM patch_groups WHERE id = ?').get(req.params.id);
    if (!existing) return res.status(404).json({ error: 'Nicht gefunden' });
    const { name, description, color, target_agent_version } = req.body;
    db.prepare('UPDATE patch_groups SET name=?, description=?, color=?, target_agent_version=? WHERE id=?')
        .run(name?.trim() ?? existing.name, description ?? existing.description, color ?? existing.color,
            target_agent_version !== undefined ? (target_agent_version || null) : existing.target_agent_version,
            req.params.id);
    res.json(db.prepare('SELECT * FROM patch_groups WHERE id = ?').get(req.params.id));
};

// Rollout-Reihenfolge: Test → Pilot → Produktion
// Eine Gruppe darf eine Version nur bekommen wenn die Vorgänger-Gruppe sie bereits hat
const ROLLOUT_ORDER = ['Test', 'Pilot', 'Produktion'];

const releaseVersionToAll = (req, res) => {
    const db = getDatabase();
    const { version } = req.body;
    if (!version) return res.status(400).json({ error: 'version erforderlich' });
    db.prepare('UPDATE patch_groups SET target_agent_version=?').run(version);
    res.json({ success: true, version });
};

const releaseVersionToGroup = (req, res) => {
    const db = getDatabase();
    const { version } = req.body;
    if (!version) return res.status(400).json({ error: 'version erforderlich' });

    const group = db.prepare('SELECT * FROM patch_groups WHERE id = ?').get(req.params.id);
    if (!group) return res.status(404).json({ error: 'Gruppe nicht gefunden' });

    const pos = ROLLOUT_ORDER.indexOf(group.name);

    // Vorgänger-Gruppe muss dieselbe oder höhere Version haben
    if (pos > 0) {
        const predecessorName = ROLLOUT_ORDER[pos - 1];
        const predecessor = db.prepare('SELECT target_agent_version FROM patch_groups WHERE name = ?').get(predecessorName);
        if (!predecessor?.target_agent_version || predecessor.target_agent_version !== version) {
            return res.status(403).json({
                error: `Version ${version} muss zuerst für "${predecessorName}" freigegeben sein bevor "${group.name}" aktualisiert werden kann.`,
                blocked_by: predecessorName,
                predecessor_version: predecessor?.target_agent_version || null
            });
        }
    }

    db.prepare('UPDATE patch_groups SET target_agent_version=? WHERE id=?').run(version, req.params.id);
    res.json({ success: true, group_id: group.id, group_name: group.name, version });
};

const deleteGroup = (req, res) => {
    const db = getDatabase();
    if (!db.prepare('SELECT id FROM patch_groups WHERE id = ?').get(req.params.id)) {
        return res.status(404).json({ error: 'Nicht gefunden' });
    }
    db.prepare('DELETE FROM patch_groups WHERE id = ?').run(req.params.id);
    res.json({ success: true });
};

// ── Patch Policies ────────────────────────────────────────────────────────────

const upsertPolicy = (req, res) => {
    const { group_id, severity, max_days, notify_email } = req.body;
    if (!group_id || !severity || !max_days) return res.status(400).json({ error: 'group_id, severity und max_days erforderlich' });
    const db = getDatabase();
    const existing = db.prepare('SELECT id FROM patch_policies WHERE group_id=? AND severity=?').get(group_id, severity);
    if (existing) {
        db.prepare('UPDATE patch_policies SET max_days=?, notify_email=? WHERE id=?')
            .run(max_days, notify_email || null, existing.id);
        res.json(db.prepare('SELECT * FROM patch_policies WHERE id=?').get(existing.id));
    } else {
        const result = db.prepare(
            'INSERT INTO patch_policies (group_id, severity, max_days, notify_email) VALUES (?,?,?,?)'
        ).run(group_id, severity, max_days, notify_email || null);
        res.status(201).json(db.prepare('SELECT * FROM patch_policies WHERE id=?').get(result.lastInsertRowid));
    }
};

const deletePolicy = (req, res) => {
    const db = getDatabase();
    db.prepare('DELETE FROM patch_policies WHERE id=?').run(req.params.id);
    res.json({ success: true });
};

// ── Agent Assignment ──────────────────────────────────────────────────────────

const assignAgent = (req, res) => {
    const { agent_id, group_id } = req.body;
    if (!agent_id || !group_id) return res.status(400).json({ error: 'agent_id und group_id erforderlich' });
    const db = getDatabase();
    // Ein Gerät kann nur in einer Gruppe sein → alte Zuweisung entfernen
    db.prepare('DELETE FROM patch_agent_groups WHERE agent_id=?').run(agent_id);
    if (group_id !== null && group_id !== 'none') {
        db.prepare('INSERT OR REPLACE INTO patch_agent_groups (agent_id, group_id) VALUES (?,?)').run(agent_id, group_id);
    }
    res.json({ success: true });
};

// ── Overview / Compliance ─────────────────────────────────────────────────────

const getOverview = (req, res) => {
    const db = getDatabase();

    const agents = db.prepare(`
        SELECT
            a.*,
            pg.id as group_id,
            pg.name as group_name,
            pg.color as group_color
        FROM monitoring_agents a
        LEFT JOIN patch_agent_groups pag ON a.id = pag.agent_id
        LEFT JOIN patch_groups pg ON pag.group_id = pg.id
        ORDER BY pg.sort_order NULLS LAST, a.hostname
    `).all();

    const policies = db.prepare('SELECT * FROM patch_policies').all();

    const result = agents.map(agent => {
        let updates = [];
        try { updates = JSON.parse(agent.pending_updates || '[]'); } catch {}

        const agentPolicies = policies.filter(p => p.group_id === agent.group_id);
        const updateCount = typeof agent.windows_updates_pending === 'number'
            ? agent.windows_updates_pending
            : 0;

        // Compliance-Status berechnen
        let compliance = 'ok';
        let violationReason = null;

        if (agentPolicies.length > 0 && updateCount > 0) {
            compliance = 'warn';
            violationReason = `${updateCount} ausstehende Update(s)`;
        }

        // last_checkin ist UTC ohne Timezone-Marker → explizit als UTC parsen
        const lastCheckin = agent.last_checkin ? new Date(agent.last_checkin + 'Z') : null;
        const isOffline = !lastCheckin || (Date.now() - lastCheckin.getTime()) > 15 * 60 * 1000;

        return {
            id: agent.id,
            hostname: agent.hostname,
            os: agent.os_name,
            os_version: agent.os_version,
            last_seen: agent.last_checkin,
            is_offline: isOffline,
            pending_updates: updateCount,
            agent_version: agent.agent_version || '?',
            tpm_present: !!agent.tpm_present,
            tpm_version: agent.tpm_version || null,
            tpm_v2: !!agent.tpm_v2,
            secure_boot: !!agent.secure_boot,
            win11_ready: !!agent.win11_ready,
            last_user: agent.last_user || null,
            bitlocker_status: agent.bitlocker_status || null,
            defender_enabled: agent.defender_enabled != null ? !!agent.defender_enabled : null,
            defender_signatures_age: agent.defender_signatures_age ?? null,
            hardware_serial: agent.hardware_serial || null,
            ip_address: agent.ip_address || null,
            group_id: agent.group_id,
            group_name: agent.group_name || 'Keine Gruppe',
            group_color: agent.group_color || '#6B7280',
            compliance,
            violation_reason: violationReason,
        };
    });

    const stats = {
        total: result.length,
        compliant: result.filter(a => a.compliance === 'ok' && !a.is_offline).length,
        warnings: result.filter(a => a.compliance === 'warn').length,
        offline: result.filter(a => a.is_offline).length,
        total_pending_updates: result.reduce((s, a) => s + (a.pending_updates || 0), 0),
    };

    res.json({ agents: result, stats });
};

// ── Commands ──────────────────────────────────────────────────────────────────

const triggerCommand = (req, res) => {
    const { agent_id, command } = req.body;
    if (!agent_id || !command) return res.status(400).json({ error: 'agent_id und command erforderlich' });
    const db = getDatabase();

    // Duplikate vermeiden: nur ein pending/sent Command pro Agent+Type
    db.prepare("DELETE FROM patch_commands WHERE agent_id=? AND command=? AND status IN ('pending','sent')").run(agent_id, command);

    const result = db.prepare(
        'INSERT INTO patch_commands (agent_id, command, triggered_by_user_id) VALUES (?,?,?)'
    ).run(agent_id, command, req.user.id);
    res.status(201).json(db.prepare('SELECT * FROM patch_commands WHERE id=?').get(result.lastInsertRowid));
};

const triggerGroupCommand = (req, res) => {
    const { group_id, command } = req.body;
    if (!group_id || !command) return res.status(400).json({ error: 'group_id und command erforderlich' });
    const db = getDatabase();

    const agents = db.prepare('SELECT agent_id FROM patch_agent_groups WHERE group_id=?').all(group_id);
    let count = 0;
    for (const { agent_id } of agents) {
        db.prepare("DELETE FROM patch_commands WHERE agent_id=? AND command=? AND status IN ('pending','sent')").run(agent_id, command);
        db.prepare('INSERT INTO patch_commands (agent_id, command, triggered_by_user_id) VALUES (?,?,?)').run(agent_id, command, req.user.id);
        count++;
    }
    res.json({ success: true, triggered: count });
};

const getCommands = (req, res) => {
    const db = getDatabase();
    const { agent_id } = req.query;
    const rows = agent_id
        ? db.prepare(`
            SELECT pc.*, a.hostname, u.username as triggered_by_username
            FROM patch_commands pc
            JOIN monitoring_agents a ON pc.agent_id = a.id
            LEFT JOIN users u ON pc.triggered_by_user_id = u.id
            WHERE pc.agent_id = ?
            ORDER BY pc.created_at DESC
            LIMIT 30
        `).all(agent_id)
        : db.prepare(`
            SELECT pc.*, a.hostname, u.username as triggered_by_username
            FROM patch_commands pc
            JOIN monitoring_agents a ON pc.agent_id = a.id
            LEFT JOIN users u ON pc.triggered_by_user_id = u.id
            ORDER BY pc.created_at DESC
            LIMIT 100
        `).all();
    res.json(rows);
};

// Called by the agent after execution
const reportCommandResult = (req, res) => {
    const agentKey = req.headers['x-agent-key'];
    if (!agentKey || agentKey !== process.env.AGENT_API_KEY) {
        return res.status(401).json({ error: 'Unauthorized' });
    }
    const command_id = req.body.command_id ?? req.body.CommandId;
    const { status, result } = req.body;
    if (command_id === undefined || command_id === null) return res.status(400).json({ error: 'command_id erforderlich' });
    const db = getDatabase();
    if (command_id !== 0) {
        db.prepare("UPDATE patch_commands SET status=?, result=?, completed_at=CURRENT_TIMESTAMP WHERE id=?")
            .run(status || 'done', result || null, command_id);
    }
    res.json({ success: true });
};

// Called by agent check-in – returns pending + running commands for this agent
const getPendingCommands = (agentId) => {
    const db = getDatabase();
    const cmds = db.prepare("SELECT * FROM patch_commands WHERE agent_id=? AND status='pending'").all(agentId);
    if (cmds.length > 0) {
        db.prepare("UPDATE patch_commands SET status='sent', sent_at=CURRENT_TIMESTAMP WHERE agent_id=? AND status='pending'")
            .run(agentId);
    }
    const running = db.prepare("SELECT * FROM patch_commands WHERE agent_id=? AND status='running'").all(agentId);
    return { pending: cmds, running };
};

// Remote Shell: POST /api/patch/shell/:agentId
const triggerShell = (req, res) => {
    const { agentId } = req.params;
    const { command } = req.body;
    if (!command) return res.status(400).json({ error: 'command erforderlich' });
    const db = getDatabase();
    const result = db.prepare(
        "INSERT INTO patch_commands (agent_id, command, params, triggered_by_user_id) VALUES (?,?,?,?)"
    ).run(parseInt(agentId), 'shell_exec', command, req.user.id);
    res.status(201).json({ id: result.lastInsertRowid });
};

// Remote Shell: GET /api/patch/shell/:agentId/result/:cmdId
const getShellResult = (req, res) => {
    const { cmdId } = req.params;
    const db = getDatabase();
    const cmd = db.prepare("SELECT id, status, result, created_at, completed_at FROM patch_commands WHERE id=?").get(parseInt(cmdId));
    if (!cmd) return res.status(404).json({ error: 'Nicht gefunden' });
    res.json(cmd);
};

// Remote Shell: GET /api/patch/shell/:agentId/history
const getShellHistory = (req, res) => {
    const { agentId } = req.params;
    const db = getDatabase();
    const cmds = db.prepare(
        "SELECT id, params, status, result, created_at, completed_at FROM patch_commands WHERE agent_id=? AND command='shell_exec' ORDER BY created_at DESC LIMIT 50"
    ).all(parseInt(agentId));
    res.json(cmds);
};

module.exports = {
    getGroups, createGroup, updateGroup, deleteGroup, releaseVersionToGroup, releaseVersionToAll,
    upsertPolicy, deletePolicy,
    assignAgent,
    getOverview,
    triggerCommand, triggerGroupCommand, getCommands, reportCommandResult,
    getPendingCommands,
    triggerShell, getShellResult, getShellHistory,
};
