// CARD Asset Ownership API Routes
// Proxies CARD operations (confirm, decline, redirect, search) and orchestrates
// the two-step update_token flow for mutations.

const express = require('express');
const pool = require('../db');
const { requireAuth, requireGroup } = require('../middleware/auth');
const logAudit = require('../helpers/auditLog');
const {
    isConfigured,
    missingVars,
    getTeams,
    getTeamAssets,
    getOwner,
    confirmAsset,
    declineAsset,
    redirectAsset,
} = require('../helpers/cardApi');

// ---------------------------------------------------------------------------
// Error classification — maps CARD API / token errors to client responses
// ---------------------------------------------------------------------------
function handleCardError(err, res) {
    const msg = err.message || String(err);
    console.error('[card-api]', msg);

    if (msg.includes('Token acquisition failed')) {
        if (msg.includes('HTTP 401')) {
            return res.status(401).json({ error: 'CARD authorization failed. Check service account credentials.' });
        }
        if (msg.includes('HTTP 403')) {
            return res.status(403).json({ error: 'CARD access denied. The service account may not be onboarded with the CARD team.' });
        }
        if (msg.includes('HTTP 525')) {
            return res.status(502).json({ error: 'CARD LDAP error. The service account may not be provisioned correctly.' });
        }
    }

    if (msg.includes('401')) {
        return res.status(401).json({ error: 'CARD token expired or invalid. The request has been retried once automatically.' });
    }
    if (msg.includes('403')) {
        return res.status(403).json({ error: 'Insufficient CARD permissions for this operation.' });
    }

    return res.status(502).json({ error: 'CARD API request failed.', details: msg });
}

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

    // GET /status
    router.get('/status', requireAuth(), (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ configured: false, error: 'CARD API is not configured.', missingVars });
        }
        res.json({ configured: true });
    });

    // GET /teams
    router.get('/teams', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        try {
            const result = await getTeams();
            if (result.ok) {
                let body;
                try { body = JSON.parse(result.body); } catch (_) { body = result.body; }
                const teams = Array.isArray(body) ? body : (body && body.teams) || [];
                return res.json(teams);
            }
            let errorBody;
            try { errorBody = JSON.parse(result.body); } catch (_) { errorBody = { error: result.body }; }
            return res.status(result.status).json(errorBody);
        } catch (err) {
            return handleCardError(err, res);
        }
    });

    // GET /teams/:teamName/assets
    router.get('/teams/:teamName/assets', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        const { teamName } = req.params;
        const { disposition, page, page_size } = req.query;

        if (!disposition) {
            return res.status(400).json({ error: 'disposition query parameter is required.' });
        }

        try {
            const result = await getTeamAssets(teamName, {
                disposition,
                page: page ? parseInt(page, 10) : undefined,
                pageSize: page_size ? parseInt(page_size, 10) : 50,
            });

            if (result.ok) {
                let body;
                try { body = JSON.parse(result.body); } catch (_) { body = result.body; }

                let resultCount = 0;
                if (body && typeof body === 'object' && typeof body.total === 'number') {
                    resultCount = body.total;
                } else if (body && Array.isArray(body.assets)) {
                    resultCount = body.assets.length;
                }
                logAudit({
                    userId: req.user.id,
                    username: req.user.username,
                    action: 'card_search',
                    entityType: 'card_asset',
                    entityId: teamName,
                    details: { disposition, resultCount },
                    ipAddress: req.ip,
                });

                return res.json(body);
            }

            let errorBody;
            try { errorBody = JSON.parse(result.body); } catch (_) { errorBody = { error: result.body }; }
            return res.status(result.status).json(errorBody);
        } catch (err) {
            return handleCardError(err, res);
        }
    });

    // GET /owner/:assetId
    router.get('/owner/:assetId', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        const { assetId } = req.params;

        try {
            const result = await getOwner(assetId);
            if (result.ok) {
                let body;
                try { body = JSON.parse(result.body); } catch (_) { body = result.body; }
                return res.json(body);
            }
            let errorBody;
            try { errorBody = JSON.parse(result.body); } catch (_) { errorBody = { error: result.body }; }
            return res.status(result.status).json(errorBody);
        } catch (err) {
            return handleCardError(err, res);
        }
    });

    // POST /queue/:queueItemId/confirm
    router.post('/queue/:queueItemId/confirm', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        const { queueItemId } = req.params;
        const { teamName, assetId, comment } = req.body;

        if (!teamName || typeof teamName !== 'string' || !teamName.trim()) {
            return res.status(400).json({ error: 'teamName is required.' });
        }
        if (!assetId || typeof assetId !== 'string' || !assetId.trim()) {
            return res.status(400).json({ error: 'assetId is required.' });
        }

        try {
            const { rows } = await pool.query(
                'SELECT * FROM ivanti_todo_queue WHERE id = $1 AND user_id = $2 AND workflow_type = $3',
                [queueItemId, req.user.id, 'CARD']
            );
            const item = rows[0];

            if (!item) {
                return res.status(404).json({ error: 'Queue item not found.' });
            }
            if (item.status !== 'pending') {
                return res.status(400).json({ error: 'Only pending queue items can be executed.' });
            }

            const ownerResult = await getOwner(assetId);
            if (!ownerResult.ok) {
                const errMsg = `Failed to fetch owner record: HTTP ${ownerResult.status}`;
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'confirm', assetId, error: errMsg, cardStatus: ownerResult.status }, ipAddress: req.ip });
                let errorBody;
                try { errorBody = JSON.parse(ownerResult.body); } catch (_) { errorBody = { error: ownerResult.body }; }
                return res.status(ownerResult.status).json(errorBody);
            }

            let ownerData;
            try { ownerData = JSON.parse(ownerResult.body); } catch (_) { ownerData = {}; }
            const updateToken = ownerData.owner && ownerData.owner.update_token;

            if (!updateToken) {
                const errMsg = 'update_token not found in owner record.';
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'confirm', assetId, error: errMsg, cardStatus: null }, ipAddress: req.ip });
                return res.status(502).json({ error: 'CARD API request failed.', details: errMsg });
            }

            const confirmResult = await confirmAsset(assetId, teamName.trim(), updateToken, comment || '');

            if (confirmResult.ok) {
                await pool.query(
                    "UPDATE ivanti_todo_queue SET status = 'complete', updated_at = NOW() WHERE id = $1",
                    [queueItemId]
                );

                let cardResponse;
                try { cardResponse = JSON.parse(confirmResult.body); } catch (_) { cardResponse = confirmResult.body; }

                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_confirm', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { assetId, teamName: teamName.trim(), comment: comment || '', cardStatus: confirmResult.status }, ipAddress: req.ip });

                return res.json({ success: true, cardResponse });
            }

            const errMsg = `Confirm failed: HTTP ${confirmResult.status}`;
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'confirm', assetId, error: errMsg, cardStatus: confirmResult.status }, ipAddress: req.ip });
            let errorBody;
            try { errorBody = JSON.parse(confirmResult.body); } catch (_) { errorBody = { error: confirmResult.body }; }
            return res.status(confirmResult.status).json(errorBody);
        } catch (err) {
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'confirm', assetId, error: err.message, cardStatus: null }, ipAddress: req.ip });
            return handleCardError(err, res);
        }
    });

    // POST /queue/:queueItemId/decline
    router.post('/queue/:queueItemId/decline', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        const { queueItemId } = req.params;
        const { teamName, assetId, comment } = req.body;

        if (!teamName || typeof teamName !== 'string' || !teamName.trim()) {
            return res.status(400).json({ error: 'teamName is required.' });
        }
        if (!assetId || typeof assetId !== 'string' || !assetId.trim()) {
            return res.status(400).json({ error: 'assetId is required.' });
        }

        try {
            const { rows } = await pool.query(
                'SELECT * FROM ivanti_todo_queue WHERE id = $1 AND user_id = $2 AND workflow_type = $3',
                [queueItemId, req.user.id, 'CARD']
            );
            const item = rows[0];

            if (!item) {
                return res.status(404).json({ error: 'Queue item not found.' });
            }
            if (item.status !== 'pending') {
                return res.status(400).json({ error: 'Only pending queue items can be executed.' });
            }

            const ownerResult = await getOwner(assetId);
            if (!ownerResult.ok) {
                const errMsg = `Failed to fetch owner record: HTTP ${ownerResult.status}`;
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'decline', assetId, error: errMsg, cardStatus: ownerResult.status }, ipAddress: req.ip });
                let errorBody;
                try { errorBody = JSON.parse(ownerResult.body); } catch (_) { errorBody = { error: ownerResult.body }; }
                return res.status(ownerResult.status).json(errorBody);
            }

            let ownerData;
            try { ownerData = JSON.parse(ownerResult.body); } catch (_) { ownerData = {}; }
            const updateToken = ownerData.owner && ownerData.owner.update_token;

            if (!updateToken) {
                const errMsg = 'update_token not found in owner record.';
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'decline', assetId, error: errMsg, cardStatus: null }, ipAddress: req.ip });
                return res.status(502).json({ error: 'CARD API request failed.', details: errMsg });
            }

            const declineResult = await declineAsset(assetId, teamName.trim(), updateToken, comment || '');

            if (declineResult.ok) {
                await pool.query(
                    "UPDATE ivanti_todo_queue SET status = 'complete', updated_at = NOW() WHERE id = $1",
                    [queueItemId]
                );

                let cardResponse;
                try { cardResponse = JSON.parse(declineResult.body); } catch (_) { cardResponse = declineResult.body; }

                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_decline', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { assetId, teamName: teamName.trim(), comment: comment || '', cardStatus: declineResult.status }, ipAddress: req.ip });

                return res.json({ success: true, cardResponse });
            }

            const errMsg = `Decline failed: HTTP ${declineResult.status}`;
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'decline', assetId, error: errMsg, cardStatus: declineResult.status }, ipAddress: req.ip });
            let errorBody;
            try { errorBody = JSON.parse(declineResult.body); } catch (_) { errorBody = { error: declineResult.body }; }
            return res.status(declineResult.status).json(errorBody);
        } catch (err) {
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'decline', assetId, error: err.message, cardStatus: null }, ipAddress: req.ip });
            return handleCardError(err, res);
        }
    });

    // POST /queue/:queueItemId/redirect
    router.post('/queue/:queueItemId/redirect', requireAuth(), requireGroup('Admin', 'Standard_User'), async (req, res) => {
        if (!isConfigured) {
            return res.status(503).json({ error: 'CARD API is not configured.', missingVars });
        }

        const { queueItemId } = req.params;
        const { fromTeam, toTeam, assetId } = req.body;

        if (!fromTeam || typeof fromTeam !== 'string' || !fromTeam.trim()) {
            return res.status(400).json({ error: 'fromTeam is required.' });
        }
        if (!toTeam || typeof toTeam !== 'string' || !toTeam.trim()) {
            return res.status(400).json({ error: 'toTeam is required.' });
        }
        if (!assetId || typeof assetId !== 'string' || !assetId.trim()) {
            return res.status(400).json({ error: 'assetId is required.' });
        }

        try {
            const { rows } = await pool.query(
                'SELECT * FROM ivanti_todo_queue WHERE id = $1 AND user_id = $2 AND workflow_type = $3',
                [queueItemId, req.user.id, 'CARD']
            );
            const item = rows[0];

            if (!item) {
                return res.status(404).json({ error: 'Queue item not found.' });
            }
            if (item.status !== 'pending') {
                return res.status(400).json({ error: 'Only pending queue items can be executed.' });
            }

            const ownerResult = await getOwner(assetId);
            if (!ownerResult.ok) {
                const errMsg = `Failed to fetch owner record: HTTP ${ownerResult.status}`;
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'redirect', assetId, error: errMsg, cardStatus: ownerResult.status }, ipAddress: req.ip });
                let errorBody;
                try { errorBody = JSON.parse(ownerResult.body); } catch (_) { errorBody = { error: ownerResult.body }; }
                return res.status(ownerResult.status).json(errorBody);
            }

            let ownerData;
            try { ownerData = JSON.parse(ownerResult.body); } catch (_) { ownerData = {}; }
            const updateToken = ownerData.owner && ownerData.owner.update_token;

            if (!updateToken) {
                const errMsg = 'update_token not found in owner record.';
                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'redirect', assetId, error: errMsg, cardStatus: null }, ipAddress: req.ip });
                return res.status(502).json({ error: 'CARD API request failed.', details: errMsg });
            }

            const redirectResult = await redirectAsset(assetId, fromTeam.trim(), toTeam.trim(), updateToken);

            if (redirectResult.ok) {
                await pool.query(
                    "UPDATE ivanti_todo_queue SET status = 'complete', updated_at = NOW() WHERE id = $1",
                    [queueItemId]
                );

                let cardResponse;
                try { cardResponse = JSON.parse(redirectResult.body); } catch (_) { cardResponse = redirectResult.body; }

                logAudit({ userId: req.user.id, username: req.user.username, action: 'card_redirect', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { assetId, fromTeam: fromTeam.trim(), toTeam: toTeam.trim(), cardStatus: redirectResult.status }, ipAddress: req.ip });

                return res.json({ success: true, cardResponse });
            }

            const errMsg = `Redirect failed: HTTP ${redirectResult.status}`;
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'redirect', assetId, error: errMsg, cardStatus: redirectResult.status }, ipAddress: req.ip });
            let errorBody;
            try { errorBody = JSON.parse(redirectResult.body); } catch (_) { errorBody = { error: redirectResult.body }; }
            return res.status(redirectResult.status).json(errorBody);
        } catch (err) {
            logAudit({ userId: req.user.id, username: req.user.username, action: 'card_action_failed', entityType: 'ivanti_todo_queue', entityId: String(queueItemId), details: { actionType: 'redirect', assetId, error: err.message, cardStatus: null }, ipAddress: req.ip });
            return handleCardError(err, res);
        }
    });

    return router;
}

module.exports = createCardApiRouter;
