/**
 * API Routes for Test Data Database
 *
 * PostgreSQL version - uses pg.Pool via database/db.js.
 * All route handlers are async. FTS uses tsvector/plainto_tsquery.
 */

const express = require('express');
const path = require('path');
const db = require('../database/db');
const { generateDatasheet } = require('../templates/datasheet');

const router = express.Router();

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
const MAX_LIMIT = 1000;

function clampLimit(value) {
    const parsed = parseInt(value, 10);
    if (isNaN(parsed) || parsed < 1) return 100;
    return Math.min(parsed, MAX_LIMIT);
}

function clampOffset(value) {
    const parsed = parseInt(value, 10);
    if (isNaN(parsed) || parsed < 0) return 0;
    return parsed;
}

// ---------------------------------------------------------------------------
// GET /api/search
// Search test records
// Query params: serial, model, from, to, result, q, station, logtype, web_status, limit, offset
// ---------------------------------------------------------------------------
router.get('/search', async (req, res) => {
    try {
        const { serial, model, from, to, result, q, station, logtype, workorder, web_status } = req.query;
        const limit = clampLimit(req.query.limit || 100);
        const offset = clampOffset(req.query.offset || 0);

        const conditions = [];
        const params = [];
        let paramIdx = 0;

        const addParam = (val) => {
            paramIdx++;
            params.push(val);
            return '$' + paramIdx;
        };

        if (q) {
            // Full-text search using tsvector
            conditions.push(`search_vector @@ plainto_tsquery('english', ${addParam(q)})`);
        }

        if (serial) {
            const val = serial.includes('%') ? serial : `%${serial}%`;
            conditions.push(`serial_number LIKE ${addParam(val)}`);
        }

        if (workorder) {
            conditions.push(`work_order = ${addParam(workorder)}`);
        }

        if (model) {
            const val = model.includes('%') ? model : `%${model}%`;
            conditions.push(`model_number LIKE ${addParam(val)}`);
        }

        if (from) {
            conditions.push(`test_date >= ${addParam(from)}`);
        }

        if (to) {
            conditions.push(`test_date <= ${addParam(to)}`);
        }

        if (result) {
            conditions.push(`overall_result = ${addParam(result.toUpperCase())}`);
        }

        if (station) {
            conditions.push(`test_station = ${addParam(station)}`);
        }

        if (logtype) {
            conditions.push(`log_type = ${addParam(logtype)}`);
        }

        if (req.query.web_status === 'off') {
            conditions.push('api_uploaded_at IS NULL');
        } else if (req.query.web_status === 'on') {
            conditions.push('api_uploaded_at IS NOT NULL');
        }

        const where = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';

        const dataSql = `SELECT * FROM test_records ${where} ORDER BY test_date DESC, serial_number LIMIT ${addParam(limit)} OFFSET ${addParam(offset)}`;
        const countSql = `SELECT COUNT(*) as count FROM test_records ${where}`;
        const countParams = params.slice(0, paramIdx - 2); // exclude limit/offset

        const [records, countRow] = await Promise.all([
            db.query(dataSql, params),
            db.queryOne(countSql, countParams),
        ]);

        res.json({
            records,
            total: countRow?.count ? parseInt(countRow.count, 10) : records.length,
            limit,
            offset
        });
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [SEARCH ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/record/:id
// Get single record by ID
// ---------------------------------------------------------------------------
router.get('/record/:id', async (req, res) => {
    try {
        const record = await db.queryOne('SELECT * FROM test_records WHERE id = $1', [req.params.id]);

        if (!record) {
            return res.status(404).json({ error: 'Record not found' });
        }

        res.json(record);
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [RECORD ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/datasheet/:id
// Generate datasheet for a record
// Query params: format (html, txt)
// ---------------------------------------------------------------------------
router.get('/datasheet/:id', async (req, res) => {
    try {
        const record = await db.queryOne('SELECT * FROM test_records WHERE id = $1', [req.params.id]);

        if (!record) {
            return res.status(404).json({ error: 'Record not found' });
        }

        const format = req.query.format || 'html';

        // Try exact-match formatter first
        const { loadAllSpecs, getSpecs } = require('../parsers/spec-reader');
        const { generateExactDatasheet } = require('../templates/datasheet-exact');
        const specMap = loadAllSpecs();
        const specs = getSpecs(specMap, record.model_number);
        const exactTxt = generateExactDatasheet(record, specs);

        if (exactTxt && format === 'html') {
            // Render exact-match TXT as styled HTML page
            const escaped = exactTxt
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;');
            const html = `<!DOCTYPE html>
<html>
<head>
    <title>Test Data Sheet - ${record.serial_number}</title>
    <style>
        body {
            margin: 0;
            padding: 20px;
            background: #f0f0f0;
            display: flex;
            justify-content: center;
        }
        .page {
            background: white;
            padding: 40px 30px;
            max-width: 720px;
            width: 100%;
            box-shadow: 0 2px 8px rgba(0,0,0,0.15);
            border: 1px solid #ccc;
        }
        pre {
            font-family: 'Courier New', Courier, monospace;
            font-size: 11px;
            line-height: 1.4;
            margin: 0;
            white-space: pre;
            overflow-x: auto;
        }
        .toolbar {
            position: fixed;
            top: 10px;
            right: 10px;
            display: flex;
            gap: 8px;
        }
        .toolbar button {
            padding: 8px 16px;
            border: 1px solid #999;
            background: white;
            cursor: pointer;
            font-size: 13px;
            border-radius: 4px;
        }
        .toolbar button:hover { background: #e0e0e0; }
        @media print {
            body { background: white; padding: 0; }
            .page { box-shadow: none; border: none; padding: 0; }
            .toolbar { display: none; }
        }
    </style>
</head>
<body>
    <div class="toolbar">
        <button onclick="window.print()">Print</button>
        <button onclick="window.open('/api/datasheet/${record.id}/pdf')">Download PDF</button>
        <button onclick="window.close()">Close</button>
    </div>
    <div class="page">
        <pre>${escaped}</pre>
    </div>
</body>
</html>`;
            res.type('html').send(html);
        } else if (exactTxt && format === 'txt') {
            res.type('text/plain').send(exactTxt);
        } else {
            // Fall back to generic template
            const datasheet = generateDatasheet(record, format);
            if (format === 'html') {
                res.type('html').send(datasheet);
            } else {
                res.type('text/plain').send(datasheet);
            }
        }
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [DATASHEET ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/datasheet/:id/pdf
// Generate PDF datasheet for a record (on-demand download)
// ---------------------------------------------------------------------------
router.get('/datasheet/:id/pdf', async (req, res) => {
    try {
        const record = await db.queryOne('SELECT * FROM test_records WHERE id = $1', [req.params.id]);

        if (!record) {
            return res.status(404).json({ error: 'Record not found' });
        }

        const { loadAllSpecs, getSpecs } = require('../parsers/spec-reader');
        const { generateExactDatasheet } = require('../templates/datasheet-exact');
        const PDFDocument = require('pdfkit');

        const specMap = loadAllSpecs();
        const specs = getSpecs(specMap, record.model_number);
        let txt = generateExactDatasheet(record, specs);

        // Fall back to generic datasheet if exact-match formatter doesn't support this family
        if (!txt) {
            txt = generateDatasheet(record, 'txt');
        }

        if (!txt) {
            return res.status(422).json({ error: 'Could not generate datasheet (missing specs or data)' });
        }

        const doc = new PDFDocument({
            size: 'LETTER',
            margins: { top: 36, bottom: 36, left: 36, right: 36 }
        });

        res.setHeader('Content-Type', 'application/pdf');
        res.setHeader('Content-Disposition', `attachment; filename="${record.serial_number}.pdf"`);
        doc.pipe(res);

        doc.font('Courier').fontSize(9.5);
        const lines = txt.split(/\r?\n/);
        for (const line of lines) {
            doc.text(line, { lineGap: 1 });
        }

        doc.end();
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [PDF ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/stats
// Get database statistics
// ---------------------------------------------------------------------------
router.get('/stats', async (req, res) => {
    try {
        const [totalRow, byLogType, byResult, byStation, dateRange, recentSerials] = await Promise.all([
            db.queryOne('SELECT COUNT(*) as count FROM test_records'),
            db.query('SELECT log_type, COUNT(*) as count FROM test_records GROUP BY log_type ORDER BY count DESC'),
            db.query('SELECT overall_result, COUNT(*) as count FROM test_records GROUP BY overall_result'),
            db.query(`SELECT test_station, COUNT(*) as count FROM test_records
                      WHERE test_station IS NOT NULL AND test_station != ''
                      GROUP BY test_station ORDER BY test_station`),
            db.queryOne('SELECT MIN(test_date) as oldest, MAX(test_date) as newest FROM test_records'),
            db.query(`SELECT DISTINCT serial_number, model_number, test_date
                      FROM test_records ORDER BY test_date DESC LIMIT 10`),
        ]);

        res.json({
            total_records: parseInt(totalRow.count, 10),
            by_log_type: byLogType.map(r => ({ ...r, count: parseInt(r.count, 10) })),
            by_result: byResult.map(r => ({ ...r, count: parseInt(r.count, 10) })),
            by_station: byStation.map(r => ({ ...r, count: parseInt(r.count, 10) })),
            date_range: dateRange,
            recent_serials: recentSerials,
        });
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [STATS ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/filters
// Get available filter options (test stations, log types, models)
// ---------------------------------------------------------------------------
router.get('/filters', async (req, res) => {
    try {
        const [stations, logTypes, models] = await Promise.all([
            db.query(`SELECT DISTINCT test_station FROM test_records
                      WHERE test_station IS NOT NULL AND test_station != ''
                      ORDER BY test_station`),
            db.query('SELECT DISTINCT log_type FROM test_records ORDER BY log_type'),
            db.query(`SELECT DISTINCT model_number, COUNT(*) as count FROM test_records
                      GROUP BY model_number ORDER BY count DESC LIMIT 500`),
        ]);

        res.json({
            stations: stations.map(r => r.test_station),
            log_types: logTypes.map(r => r.log_type),
            models: models.map(r => ({ ...r, count: parseInt(r.count, 10) })),
        });
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [FILTERS ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/export
// Export search results as CSV
// ---------------------------------------------------------------------------
router.get('/export', async (req, res) => {
    try {
        const { serial, model, from, to, result, station, logtype } = req.query;

        const conditions = [];
        const params = [];
        let paramIdx = 0;

        const addParam = (val) => {
            paramIdx++;
            params.push(val);
            return '$' + paramIdx;
        };

        if (serial) {
            const val = serial.includes('%') ? serial : `%${serial}%`;
            conditions.push(`serial_number LIKE ${addParam(val)}`);
        }

        if (model) {
            const val = model.includes('%') ? model : `%${model}%`;
            conditions.push(`model_number LIKE ${addParam(val)}`);
        }

        if (from) {
            conditions.push(`test_date >= ${addParam(from)}`);
        }

        if (to) {
            conditions.push(`test_date <= ${addParam(to)}`);
        }

        if (result) {
            conditions.push(`overall_result = ${addParam(result.toUpperCase())}`);
        }

        if (station) {
            conditions.push(`test_station = ${addParam(station)}`);
        }

        if (logtype) {
            conditions.push(`log_type = ${addParam(logtype)}`);
        }

        if (req.query.web_status === 'off') {
            conditions.push('api_uploaded_at IS NULL');
        } else if (req.query.web_status === 'on') {
            conditions.push('api_uploaded_at IS NOT NULL');
        }

        const where = conditions.length > 0 ? 'WHERE ' + conditions.join(' AND ') : '';
        const sql = `SELECT * FROM test_records ${where} ORDER BY test_date DESC, serial_number LIMIT 10000`;

        const records = await db.query(sql, params);

        // Generate CSV
        const headers = ['id', 'log_type', 'model_number', 'serial_number', 'test_date', 'test_station', 'overall_result', 'source_file'];
        let csv = headers.join(',') + '\n';

        for (const record of records) {
            const row = headers.map(h => {
                const val = record[h] || '';
                return `"${String(val).replace(/"/g, '""')}"`;
            });
            csv += row.join(',') + '\n';
        }

        res.setHeader('Content-Type', 'text/csv');
        res.setHeader('Content-Disposition', 'attachment; filename=test_records.csv');
        res.send(csv);
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [EXPORT ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/workorder/:wo
// Get work order details and all associated test lines
// ---------------------------------------------------------------------------
router.get('/workorder/:wo', async (req, res) => {
    try {
        const wo = req.params.wo;

        const [header, lines, testRecords] = await Promise.all([
            db.queryOne('SELECT * FROM work_orders WHERE wo_number = $1', [wo]),
            db.query('SELECT * FROM work_order_lines WHERE wo_number = $1 ORDER BY test_date, test_time', [wo]),
            db.query(
                'SELECT id, log_type, model_number, serial_number, test_date, test_station, overall_result, work_order FROM test_records WHERE work_order = $1 ORDER BY serial_number',
                [wo]
            ),
        ]);

        res.json({
            work_order: header || { wo_number: wo },
            lines,
            test_records: testRecords,
        });
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [WO ERROR] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// GET /api/workorder-search?q=<query>
// Search work orders by number (prefix match)
// ---------------------------------------------------------------------------
router.get('/workorder-search', async (req, res) => {
    try {
        const q = req.query.q || '';
        if (q.length < 2) {
            return res.json({ results: [] });
        }

        const results = await db.query(
            'SELECT wo_number, wo_date, program, test_station FROM work_orders WHERE wo_number LIKE $1 ORDER BY wo_date DESC LIMIT 50',
            [q + '%']
        );

        res.json({ results });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// ---------------------------------------------------------------------------
// Cleanup function for graceful shutdown
// ---------------------------------------------------------------------------
async function cleanup() {
    try {
        await db.close();
    } catch (err) {
        console.error(`[${new Date().toISOString()}] [CLEANUP ERROR] ${err.message}`);
    }
}

/**
 * POST /api/upload
 *
 * Body: { ids?: number[], serialNumbers?: string[], all_unuploaded?: boolean }
 *
 * Pushes selected records to the Dataforth website API. Accepts either a set
 * of record IDs (resolved to serial_number + checked for exported status), a
 * direct list of serial numbers, or all_unuploaded:true to push every PASS
 * record where api_uploaded_at IS NULL.
 *
 * Response: { created, updated, unchanged, errors, skipped, processed, sns }
 */
router.post('/upload', async (req, res) => {
    try {
        const { ids, serialNumbers, all_unuploaded } = req.body || {};
        const { uploadBySerialNumbers } = require('../database/upload-to-api');

        let sns = [];
        if (all_unuploaded) {
            const rows = await db.query(
                `SELECT DISTINCT serial_number FROM test_records
                 WHERE overall_result = 'PASS'
                   AND api_uploaded_at IS NULL
                 ORDER BY serial_number`
            );
            sns = rows.map(r => r.serial_number);
        } else if (Array.isArray(ids) && ids.length > 0) {
            const placeholders = ids.map((_, i) => `$${i + 1}`).join(',');
            const rows = await db.query(
                `SELECT DISTINCT serial_number FROM test_records
                 WHERE id IN (${placeholders})
                   AND overall_result = 'PASS'`,
                ids,
            );
            sns = rows.map(r => r.serial_number);
        } else if (Array.isArray(serialNumbers) && serialNumbers.length > 0) {
            sns = [...new Set(serialNumbers)];
        } else {
            return res.status(400).json({ error: 'provide ids[], serialNumbers[], or all_unuploaded=true' });
        }

        if (sns.length === 0) {
            return res.json({ created:0, updated:0, unchanged:0, errors:0, skipped:0, processed:0, sns:[] });
        }

        const result = await uploadBySerialNumbers(sns);
        res.json({ ...result, processed: sns.length, sns });
    } catch (err) {
        console.error(`[UPLOAD] ${err.message}`);
        res.status(500).json({ error: err.message });
    }
});

module.exports = router;
module.exports.cleanup = cleanup;
