// Ivanti / RiskSense Workflow Routes
// Data is cached in PostgreSQL and refreshed on a daily schedule or on-demand.

const express = require('express');
const pool = require('../db');
const { requireAuth, requireGroup } = require('../middleware/auth');
const { ivantiPost } = require('../helpers/ivantiApi');

const SYNC_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours

// ---------------------------------------------------------------------------
// Core sync — calls Ivanti API, stores result in PostgreSQL
// ---------------------------------------------------------------------------
async function syncWorkflows() {
    const apiKey = process.env.IVANTI_API_KEY;
    const clientId = process.env.IVANTI_CLIENT_ID || '1550';
    const skipTls = process.env.IVANTI_SKIP_TLS === 'true';

    if (!apiKey) {
        const errMsg = 'IVANTI_API_KEY not set in .env — skipping sync';
        console.warn('[Ivanti]', errMsg);
        await pool.query(
            `UPDATE ivanti_sync_state SET sync_status='error', error_message=$1, synced_at=NOW() WHERE id=1`,
            [errMsg]
        );
        return;
    }

    // Get all unique Ivanti identities from users table
    const { rows: ivantiUsers } = await pool.query(
        `SELECT DISTINCT ivanti_first_name, ivanti_last_name
         FROM users
         WHERE ivanti_first_name IS NOT NULL AND ivanti_last_name IS NOT NULL
           AND ivanti_first_name != '' AND ivanti_last_name != ''`
    );

    // Fallback to env var if no users have Ivanti identity configured
    if (ivantiUsers.length === 0) {
        const envFirst = process.env.IVANTI_FIRST_NAME || '';
        const envLast = process.env.IVANTI_LAST_NAME || '';
        if (envFirst && envLast) {
            ivantiUsers.push({ ivanti_first_name: envFirst, ivanti_last_name: envLast });
        } else {
            const errMsg = 'No Ivanti identities configured — set ivanti_first_name/ivanti_last_name on user accounts';
            console.warn('[Ivanti]', errMsg);
            await pool.query(
                `UPDATE ivanti_sync_state SET sync_status='error', error_message=$1, synced_at=NOW() WHERE id=1`,
                [errMsg]
            );
            return;
        }
    }

    console.log(`[Ivanti] Syncing workflows for ${ivantiUsers.length} user(s)...`);

    const urlPath = `/client/${encodeURIComponent(clientId)}/workflowBatch/search`;
    let allWorkflows = [];

    for (const user of ivantiUsers) {
        const body = {
            filters: [
                {
                    field: 'created_by_last_name',
                    exclusive: false,
                    operator: 'IN',
                    orWithPrevious: false,
                    implicitFilters: [],
                    value: user.ivanti_last_name,
                    caseSensitive: false
                },
                {
                    field: 'created_by_first_name',
                    exclusive: false,
                    operator: 'IN',
                    orWithPrevious: false,
                    implicitFilters: [],
                    value: user.ivanti_first_name,
                    caseSensitive: false
                }
            ],
            projection: 'internal',
            sort: [{ field: 'created', direction: 'DESC' }],
            page: 0,
            size: 50
        };

        try {
            const result = await ivantiPost(urlPath, body, apiKey, skipTls);

            if (result.status === 401) {
                throw new Error('Invalid or missing API key (401) — check IVANTI_API_KEY in .env');
            }
            if (result.status === 419) {
                throw new Error('Insufficient privileges (419) — API key lacks workflow access');
            }
            if (result.status === 429) {
                throw new Error('Rate limited (429) — will retry at next scheduled sync');
            }
            if (result.status !== 200) {
                console.error(`[Ivanti] Workflow sync for ${user.ivanti_first_name} ${user.ivanti_last_name} returned ${result.status}`);
                continue;
            }

            const data = JSON.parse(result.body);
            let workflows = [];

            if (data.page && typeof data.page.totalElements === 'number') {
                workflows = data._embedded?.workflowBatches
                    || data._embedded?.workflowBatch
                    || [];
            } else if (typeof data.total === 'number') {
                workflows = data.data || data.content || data.results || [];
            } else if (typeof data.totalElements === 'number') {
                workflows = data.content || data.data || [];
            } else if (Array.isArray(data)) {
                workflows = data;
            }

            // Tag each workflow with the Ivanti identity that owns it
            workflows.forEach(w => {
                w._ivanti_first_name = user.ivanti_first_name;
                w._ivanti_last_name = user.ivanti_last_name;
            });

            allWorkflows = allWorkflows.concat(workflows);
            console.log(`[Ivanti]   ${user.ivanti_first_name} ${user.ivanti_last_name}: ${workflows.length} workflows`);
        } catch (err) {
            console.error(`[Ivanti] Workflow sync failed for ${user.ivanti_first_name} ${user.ivanti_last_name}:`, err.message);
            // If it's a fatal error (auth), break and report
            if (err.message.includes('401') || err.message.includes('419')) {
                await pool.query(
                    `UPDATE ivanti_sync_state SET sync_status='error', error_message=$1, synced_at=NOW() WHERE id=1`,
                    [err.message]
                );
                return;
            }
        }
    }

    await pool.query(
        `UPDATE ivanti_sync_state
         SET total=$1, workflows_json=$2, synced_at=NOW(), sync_status='success', error_message=NULL
         WHERE id=1`,
        [allWorkflows.length, JSON.stringify(allWorkflows)]
    );

    console.log(`[Ivanti] Sync complete — ${allWorkflows.length} total workflows`);
}

// ---------------------------------------------------------------------------
// Scheduler — runs sync immediately if >24h stale, then every 24h
// ---------------------------------------------------------------------------
async function scheduleSync() {
    try {
        const { rows } = await pool.query('SELECT synced_at FROM ivanti_sync_state WHERE id = 1');
        const row = rows[0];
        if (!row || !row.synced_at) {
            syncWorkflows();
        } else {
            const lastSync = new Date(row.synced_at);
            const hoursSince = (Date.now() - lastSync.getTime()) / (1000 * 60 * 60);
            if (hoursSince >= 24) {
                syncWorkflows();
            } else {
                const hoursUntil = (24 - hoursSince).toFixed(1);
                console.log(`[Ivanti] Last sync ${hoursSince.toFixed(1)}h ago — next auto-sync in ${hoursUntil}h`);
            }
        }
    } catch (err) {
        console.error('[Ivanti] Schedule check failed:', err);
        syncWorkflows();
    }

    setInterval(() => syncWorkflows(), SYNC_INTERVAL_MS);
}

// ---------------------------------------------------------------------------
// Helper — read current state from DB and return as JSON-ready object
// ---------------------------------------------------------------------------
async function readState() {
    const { rows } = await pool.query(
        'SELECT total, workflows_json, synced_at, sync_status, error_message FROM ivanti_sync_state WHERE id = 1'
    );
    const row = rows[0];
    if (!row) return { total: 0, workflows: [], synced_at: null, sync_status: 'never', error_message: null };

    let workflows = [];
    try { workflows = JSON.parse(row.workflows_json || '[]'); } catch (_) { /* leave empty */ }

    return {
        total: workflows.length,
        workflows,
        synced_at: row.synced_at,
        sync_status: row.sync_status,
        error_message: row.error_message
    };
}

// ---------------------------------------------------------------------------
// Router
// ---------------------------------------------------------------------------
function createIvantiWorkflowsRouter() {
    const router = express.Router();

    // Kick off scheduler (fire-and-forget on startup)
    scheduleSync().catch((err) => console.error('[Ivanti] Init failed:', err));

    // All routes require authentication
    router.use(requireAuth());

    /**
     * GET /api/ivanti/workflows
     *
     * Returns cached Ivanti workflow data filtered by the logged-in user's
     * Ivanti identity (ivanti_first_name / ivanti_last_name on their account).
     * If the user has no Ivanti identity configured, returns all workflows (admin view).
     *
     * @returns {object} 200 - { total, workflows, synced_at, sync_status, error_message }
     * @returns {object} 500 - { error: 'Database error reading sync state' }
     */
    router.get('/', async (req, res) => {
        try {
            const state = await readState();

            // Get logged-in user's Ivanti identity
            const { rows: userRows } = await pool.query(
                'SELECT ivanti_first_name, ivanti_last_name FROM users WHERE id = $1',
                [req.user.id]
            );
            const ivantiUser = userRows[0];

            // If user has Ivanti identity, filter workflows to only theirs
            if (ivantiUser && ivantiUser.ivanti_first_name && ivantiUser.ivanti_last_name) {
                state.workflows = state.workflows.filter(w =>
                    (w._ivanti_first_name || '').toLowerCase() === ivantiUser.ivanti_first_name.toLowerCase() &&
                    (w._ivanti_last_name || '').toLowerCase() === ivantiUser.ivanti_last_name.toLowerCase()
                );
                state.total = state.workflows.length;
            }
            // If no Ivanti identity configured, show all (admin view)

            res.json(state);
        } catch {
            res.status(500).json({ error: 'Database error reading sync state' });
        }
    });

    /**
     * POST /api/ivanti/workflows/sync
     *
     * Triggers an immediate Ivanti workflow sync for all configured user identities,
     * awaits completion, and returns the updated cached state. Requires Admin or
     * Standard_User group.
     *
     * @returns {object} 200 - { total, workflows, synced_at, sync_status, error_message }
     * @returns {object} 401 - { error: 'Authentication required' }
     * @returns {object} 403 - { error: 'Insufficient permissions', required: ['Admin', 'Standard_User'], current: '...' }
     * @returns {object} 500 - { error: 'Sync ran but could not read updated state' }
     */
    router.post('/sync', requireGroup('Admin', 'Standard_User'), async (req, res) => {
        await syncWorkflows();
        try {
            res.json(await readState());
        } catch {
            res.status(500).json({ error: 'Sync ran but could not read updated state' });
        }
    });

    return router;
}

module.exports = createIvantiWorkflowsRouter;
