const express = require('express');
const http = require('http');
const https = require('https');
const { URL } = require('url');
const router = express.Router();
const db = require('../db');
const logger = require('../utils/logger');
const { verifyToken } = require('../middleware/auth');

const normalizePositiveInt = (value, fallback, min = 1, max = Number.MAX_SAFE_INTEGER) => {
    const n = Number(value);
    if (!Number.isFinite(n)) return fallback;
    const intValue = Math.trunc(n);
    if (intValue < min) return min;
    if (intValue > max) return max;
    return intValue;
};

const AI_TIMEOUT_MS = Number(process.env.AI_TIMEOUT_MS || 45000);
const AI_MAX_TOKENS = normalizePositiveInt(process.env.AI_MAX_TOKENS, 4096, 256, 8192);
const AI_DEFAULT_PROVIDER = String(process.env.AI_DEFAULT_PROVIDER || 'deepseek').trim().toLowerCase();
const AI_PROVIDER_PRIORITY = String(process.env.AI_PROVIDER_PRIORITY || 'deepseek').trim();
const AI_NAME_MAX_ITEMS_DEFAULT = normalizePositiveInt(process.env.AI_NAME_MAX_ITEMS_DEFAULT, 100, 20, 500);
const AI_NAME_MAX_ITEMS_HARD_LIMIT = normalizePositiveInt(process.env.AI_NAME_MAX_ITEMS_HARD_LIMIT, 300, 50, 1000);

const ADMIN_ROLES = new Set(['admin', 'super_admin', 'senior_user', 'intermediate_user']);
const SUPPORTED_PROVIDERS = new Set(['auto', 'deepseek']);
const SUPPORTED_MODES = new Set(['standard', 'deep']);
const SUPPORTED_NAME_STRATEGIES = new Set(['latest', 'balanced']);
const TYPE_LABEL_MAP = {
    paper: 'Paper',
    project: 'Project',
    award: 'Award',
    standard: 'Standard',
    monograph: 'Monograph',
    report: 'Technical Report',
    plan: 'Plan',
    patent: 'Patent',
    transformation: 'Transformation',
    software: 'Software Copyright'
};
const STATUS_LABEL_MAP = {
    pending: 'Pending',
    approved: 'Approved',
    rejected: 'Rejected'
};

const clampText = (value, maxLen = 600) => {
    return String(value || '').trim().slice(0, maxLen);
};

const normalizeFilter = (value) => {
    if (value === undefined || value === null) return null;
    const s = String(value).trim();
    if (!s || s.toLowerCase() === 'all') return null;
    return s;
};

const toSafeInt = (value, fallback = null) => {
    const n = Number(value);
    if (!Number.isFinite(n)) return fallback;
    return Math.trunc(n);
};

const normalizeProvider = (value) => {
    const provider = String(value || '').trim().toLowerCase();
    if (!provider) return 'auto';
    if (!SUPPORTED_PROVIDERS.has(provider)) return 'auto';
    return provider;
};

const normalizeMode = (value) => {
    const mode = String(value || '').trim().toLowerCase();
    if (!mode || !SUPPORTED_MODES.has(mode)) return 'standard';
    return mode;
};

const normalizeNameStrategy = (value) => {
    const strategy = String(value || '').trim().toLowerCase();
    if (!strategy || !SUPPORTED_NAME_STRATEGIES.has(strategy)) return 'latest';
    return strategy;
};

const normalizeNameLimit = (value) => {
    return normalizePositiveInt(value, AI_NAME_MAX_ITEMS_DEFAULT, 20, AI_NAME_MAX_ITEMS_HARD_LIMIT);
};

const toIsoDate = (value) => {
    if (!value) return null;
    const date = value instanceof Date ? value : new Date(value);
    if (!Number.isFinite(date.getTime())) return null;
    return date.toISOString().slice(0, 10);
};

const getProviderConfigs = () => {
    return {
        deepseek: {
            baseUrl: String(process.env.DEEPSEEK_API_BASE_URL || '').trim(),
            apiKey: String(process.env.DEEPSEEK_API_KEY || '').trim(),
            model: String(process.env.DEEPSEEK_MODEL || 'deepseek-chat').trim() || 'deepseek-chat'
        }
    };
};

const getAvailableProviders = (configs) => {
    return Object.keys(configs).filter((provider) => {
        const cfg = configs[provider];
        return !!(cfg && cfg.baseUrl && cfg.apiKey && cfg.model);
    });
};

const parsePriority = () => {
    const tokens = AI_PROVIDER_PRIORITY
        .split(',')
        .map((p) => normalizeProvider(p))
        .filter((p) => p !== 'auto');
    return Array.from(new Set(tokens));
};

const resolveProviderOrder = (requestedProvider, availableProviders) => {
    if (requestedProvider !== 'auto') {
        return availableProviders.includes(requestedProvider) ? [requestedProvider] : [];
    }

    const priority = parsePriority();
    const ordered = [
        ...priority.filter((p) => availableProviders.includes(p)),
        ...availableProviders.filter((p) => !priority.includes(p))
    ];

    const defaultProvider = normalizeProvider(AI_DEFAULT_PROVIDER);
    if (defaultProvider !== 'auto' && ordered.includes(defaultProvider)) {
        return [defaultProvider, ...ordered.filter((p) => p !== defaultProvider)];
    }

    return ordered;
};

const postJson = (urlString, payload, headers = {}, timeoutMs = 30000) => {
    return new Promise((resolve, reject) => {
        const url = new URL(urlString);
        const body = JSON.stringify(payload);
        const requestLib = url.protocol === 'http:' ? http : https;
        const defaultPort = url.protocol === 'http:' ? 80 : 443;

        const req = requestLib.request({
            protocol: url.protocol,
            hostname: url.hostname,
            port: url.port || defaultPort,
            path: `${url.pathname}${url.search}`,
            method: 'POST',
            timeout: timeoutMs,
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(body),
                ...headers
            }
        }, (res) => {
            let raw = '';
            res.setEncoding('utf8');
            res.on('data', (chunk) => {
                raw += chunk;
            });
            res.on('end', () => {
                const statusCode = res.statusCode || 500;
                let parsed = {};
                try {
                    parsed = raw ? JSON.parse(raw) : {};
                } catch (e) {
                    parsed = { raw };
                }

                if (statusCode >= 200 && statusCode < 300) {
                    resolve(parsed);
                } else {
                    reject(new Error(parsed?.error?.message || parsed?.message || `AI provider error: ${statusCode}`));
                }
            });
        });

        req.on('timeout', () => {
            req.destroy(new Error('AI request timeout'));
        });
        req.on('error', reject);
        req.write(body);
        req.end();
    });
};

const writeSse = (res, event, payload) => {
    if (res.writableEnded) return;
    res.write(`event: ${event}\n`);
    res.write(`data: ${JSON.stringify(payload)}\n\n`);
};

const extractChoiceText = (parsed) => {
    const choice = parsed?.choices?.[0] || {};
    if (typeof choice?.delta?.content === 'string') return choice.delta.content;
    if (typeof choice?.message?.content === 'string') return choice.message.content;
    return '';
};

const streamProviderToClient = ({ req, res, provider, config, systemPrompt, userPrompt, maxTokens = AI_MAX_TOKENS, timeoutMs = 30000 }) => {
    return new Promise((resolve, reject) => {
        const endpoint = `${config.baseUrl.replace(/\/+$/, '')}/chat/completions`;
        const url = new URL(endpoint);
        const requestLib = url.protocol === 'http:' ? http : https;
        const defaultPort = url.protocol === 'http:' ? 80 : 443;
        const payload = JSON.stringify({
            model: config.model,
            temperature: 0.2,
            max_tokens: maxTokens,
            stream: true,
            messages: [
                { role: 'system', content: systemPrompt },
                { role: 'user', content: userPrompt }
            ]
        });

        let settled = false;
        let tokenChars = 0;
        let lineBuffer = '';
        let rawFallbackBuffer = '';
        let metaSent = false;
        const onClientClose = () => {
            if (!upstreamReq.destroyed) {
                upstreamReq.destroy(new Error('client closed connection'));
            }
        };
        const settle = (fn, value) => {
            if (settled) return;
            settled = true;
            req.off('close', onClientClose);
            fn(value);
        };

        const upstreamReq = requestLib.request({
            protocol: url.protocol,
            hostname: url.hostname,
            port: url.port || defaultPort,
            path: `${url.pathname}${url.search}`,
            method: 'POST',
            timeout: timeoutMs,
            headers: {
                'Content-Type': 'application/json',
                'Content-Length': Buffer.byteLength(payload),
                Accept: 'text/event-stream',
                Authorization: `Bearer ${config.apiKey}`
            }
        }, (upstreamRes) => {
            const statusCode = upstreamRes.statusCode || 500;
            upstreamRes.setEncoding('utf8');

            if (statusCode < 200 || statusCode >= 300) {
                let errRaw = '';
                upstreamRes.on('data', (chunk) => {
                    errRaw += chunk;
                });
                upstreamRes.on('end', () => {
                    try {
                        const parsed = errRaw ? JSON.parse(errRaw) : {};
                        settle(reject, new Error(parsed?.error?.message || parsed?.message || `AI provider error: ${statusCode}`));
                    } catch (e) {
                        settle(reject, new Error(`AI provider error: ${statusCode}`));
                    }
                });
                return;
            }

            if (!metaSent) {
                writeSse(res, 'meta', {
                    provider,
                    model: config.model,
                    generatedAt: new Date().toISOString()
                });
                metaSent = true;
            }

            const flushLine = (line) => {
                const trimmed = String(line || '').trim();
                if (!trimmed || trimmed.startsWith(':') || !trimmed.startsWith('data:')) return;
                const data = trimmed.slice(5).trim();
                if (!data || data === '[DONE]') return;
                try {
                    const parsed = JSON.parse(data);
                    const deltaText = extractChoiceText(parsed);
                    if (deltaText) {
                        tokenChars += deltaText.length;
                        writeSse(res, 'token', { content: deltaText });
                    }
                } catch (e) {
                    // Ignore malformed stream chunks.
                }
            };

            upstreamRes.on('data', (chunk) => {
                rawFallbackBuffer += chunk;
                lineBuffer += chunk.replace(/\r/g, '');
                let idx = lineBuffer.indexOf('\n');
                while (idx !== -1) {
                    const line = lineBuffer.slice(0, idx);
                    lineBuffer = lineBuffer.slice(idx + 1);
                    flushLine(line);
                    idx = lineBuffer.indexOf('\n');
                }
            });

            upstreamRes.on('end', () => {
                if (lineBuffer) flushLine(lineBuffer);

                if (tokenChars === 0 && rawFallbackBuffer.trim()) {
                    try {
                        const parsed = JSON.parse(rawFallbackBuffer);
                        const wholeText = extractChoiceText(parsed);
                        if (wholeText) {
                            tokenChars += wholeText.length;
                            writeSse(res, 'token', { content: wholeText });
                        }
                    } catch (e) {
                        // Ignore fallback parse errors.
                    }
                }

                if (tokenChars === 0) {
                    settle(reject, new Error('provider returned empty analysis'));
                    return;
                }
                settle(resolve, { provider, model: config.model, tokenChars });
            });

            upstreamRes.on('error', (err) => {
                settle(reject, err);
            });
        });

        upstreamReq.on('timeout', () => {
            upstreamReq.destroy(new Error('AI request timeout'));
        });

        upstreamReq.on('error', (err) => {
            settle(reject, err);
        });

        req.once('close', onClientClose);

        upstreamReq.write(payload);
        upstreamReq.end();
    });
};

const buildWhereClause = (filters = {}) => {
    const where = ['1=1'];
    const params = [];

    const year = toSafeInt(normalizeFilter(filters.year));
    const type = normalizeFilter(filters.type);
    const status = normalizeFilter(filters.status);
    const department = normalizeFilter(filters.department);

    if (year && year >= 2000 && year <= 2100) {
        where.push(`EXTRACT(YEAR FROM achievement_date) = $${params.length + 1}`);
        params.push(year);
    }
    if (type) {
        where.push(`type = $${params.length + 1}`);
        params.push(type);
    }
    if (status) {
        where.push(`status = $${params.length + 1}`);
        params.push(status);
    }

    return { whereSql: where.join(' AND '), params, department };
};

const toNumberRows = (rows = []) => {
    return rows.map((row) => ({
        ...row,
        count: Number(row.count || 0)
    }));
};

const toPercent = (count, total) => {
    if (!total) return 0;
    return Number(((Number(count || 0) / Number(total || 1)) * 100).toFixed(1));
};

const formatFilterValue = (key, value) => {
    if (!value || value === 'all') return '鍏ㄩ儴';
    if (key === 'type') return TYPE_LABEL_MAP[value] || value;
    if (key === 'status') return STATUS_LABEL_MAP[value] || value;
    return value;
};

const buildPromptDataset = (dataset) => {
    const total = Number(dataset?.overview?.total || 0);
    const rawFilters = dataset?.filters || {};
    const mode = dataset?.mode === 'deep' ? 'deep' : 'standard';
    const rawNameSamples = dataset?.nameSamples || {};

    return {
        mode,
        filters: {
            year: formatFilterValue('year', rawFilters.year),
            type: formatFilterValue('type', rawFilters.type),
            status: formatFilterValue('status', rawFilters.status),
            department: formatFilterValue('department', rawFilters.department)
        },
        overview: { total },
        byType: (dataset?.byType || []).slice(0, 8).map((item) => ({
            type: TYPE_LABEL_MAP[item.type] || item.type,
            count: Number(item.count || 0),
            ratio: toPercent(item.count, total)
        })),
        byStatus: (dataset?.byStatus || []).map((item) => ({
            status: STATUS_LABEL_MAP[item.status] || item.status,
            count: Number(item.count || 0),
            ratio: toPercent(item.count, total)
        })),
        byDepartmentTop: (dataset?.byDepartment || []).slice(0, 10).map((item) => ({
            department: item.department,
            count: Number(item.count || 0),
            ratio: toPercent(item.count, total)
        })),
        monthlyRecent: (dataset?.byMonth || []).slice(-24).map((item) => ({
            month: item.month,
            count: Number(item.count || 0)
        })),
        quality: dataset?.quality || {},
        nameSamples: {
            mode,
            strategy: rawNameSamples?.strategy || 'latest',
            sent: Number(rawNameSamples?.sent || 0),
            total: Number(rawNameSamples?.total || 0),
            truncated: Boolean(rawNameSamples?.truncated),
            items: (rawNameSamples?.items || []).map((item) => ({
                name: clampText(item?.name, 120),
                type: TYPE_LABEL_MAP[item?.type] || item?.type || 'unknown',
                department: item?.department || 'unknown',
                date: item?.date || null
            }))
        }
    };
};

const gatherAnalysisDataset = async (filters = {}, options = {}) => {
    const { whereSql, params, department } = buildWhereClause(filters);
    const scopedParams = department ? [...params, department] : params;
    const scopedWhere = department ? `${whereSql} AND owner_department = $${params.length + 1}` : whereSql;

    const mode = normalizeMode(options?.mode);
    const nameStrategy = normalizeNameStrategy(options?.nameStrategy);
    const nameLimit = normalizeNameLimit(options?.nameLimit);

    const ownerAchievementsCte = `
        WITH leader_depts AS (
            SELECT name FROM dict_leader_departments
        ),
        leader_dept_array AS (
            SELECT COALESCE(array_agg(name::text), ARRAY[]::text[]) AS names FROM leader_depts
        ),
        owner_achievements AS (
            SELECT
                a.id,
                a.name,
                a.type,
                a.status,
                a.achievement_date,
                a.contributors,
                a.assigned_departments,
                a.remarks,
                COALESCE(non_leader.dept, first_any.dept) AS owner_department
            FROM achievements a
            CROSS JOIN leader_dept_array lda
            LEFT JOIN LATERAL (
                SELECT u.dept
                FROM unnest(COALESCE(a.assigned_departments::text[], ARRAY[]::text[])) WITH ORDINALITY AS u(dept, ord)
                WHERE u.dept IS NOT NULL
                  AND BTRIM(u.dept) <> ''
                  AND u.dept <> ALL(lda.names)
                ORDER BY u.ord
                LIMIT 1
            ) non_leader ON TRUE
            LEFT JOIN LATERAL (
                SELECT u.dept
                FROM unnest(COALESCE(a.assigned_departments::text[], ARRAY[]::text[])) WITH ORDINALITY AS u(dept, ord)
                WHERE u.dept IS NOT NULL
                  AND BTRIM(u.dept) <> ''
                ORDER BY u.ord
                LIMIT 1
            ) first_any ON TRUE
        )
    `;

    const totalRes = await db.query(
        `${ownerAchievementsCte}
         SELECT COUNT(*)::int AS count
         FROM owner_achievements
         WHERE ${scopedWhere}`,
        scopedParams
    );
    const typeRes = await db.query(
        `${ownerAchievementsCte}
         SELECT type, COUNT(*)::int AS count
         FROM owner_achievements
         WHERE ${scopedWhere}
         GROUP BY type
         ORDER BY count DESC`,
        scopedParams
    );
    const statusRes = await db.query(
        `${ownerAchievementsCte}
         SELECT status, COUNT(*)::int AS count
         FROM owner_achievements
         WHERE ${scopedWhere}
         GROUP BY status
         ORDER BY count DESC`,
        scopedParams
    );
    const deptRes = await db.query(
        `${ownerAchievementsCte},
         department_scope AS (
            SELECT d.name AS department
            FROM departments d, leader_dept_array lda
            WHERE d.name::text <> ALL(lda.names)
         )
         SELECT ds.department, COUNT(oa.id)::int AS count
         FROM department_scope ds
         LEFT JOIN owner_achievements oa
           ON ds.department = oa.owner_department
          AND (${scopedWhere})
         GROUP BY ds.department
         ORDER BY count DESC
         LIMIT 20`,
        scopedParams
    );
    const monthlyRes = await db.query(
        `${ownerAchievementsCte}
         SELECT month, count
         FROM (
           SELECT TO_CHAR(achievement_date, 'YYYY-MM') AS month, COUNT(*)::int AS count
           FROM owner_achievements
           WHERE ${scopedWhere} AND achievement_date IS NOT NULL
           GROUP BY month
           ORDER BY month DESC
           LIMIT 48
         ) t
         ORDER BY month`,
        scopedParams
    );

    const qualityRes = await db.query(
        `${ownerAchievementsCte}
         SELECT
           COUNT(*) FILTER (WHERE contributors IS NULL OR jsonb_array_length(contributors) = 0)::int AS empty_contributors,
           COUNT(*) FILTER (WHERE owner_department IS NULL)::int AS empty_departments,
           COUNT(*) FILTER (WHERE remarks IS NULL OR LENGTH(BTRIM(remarks)) = 0)::int AS empty_remarks
         FROM owner_achievements
         WHERE ${scopedWhere}`,
        scopedParams
    );

    const total = Number(totalRes.rows[0]?.count || 0);
    const byType = toNumberRows(typeRes.rows);
    const byStatus = toNumberRows(statusRes.rows);
    const byDepartment = toNumberRows(deptRes.rows);
    const byMonth = toNumberRows(monthlyRes.rows);

    const quality = {
        emptyContributors: Number(qualityRes.rows[0]?.empty_contributors || 0),
        emptyDepartments: Number(qualityRes.rows[0]?.empty_departments || 0),
        emptyRemarks: Number(qualityRes.rows[0]?.empty_remarks || 0)
    };

    let nameSamples = {
        mode,
        strategy: nameStrategy,
        sent: 0,
        total: 0,
        truncated: false,
        items: []
    };

    if (mode === 'deep') {
        const nameCountRes = await db.query(
            `${ownerAchievementsCte}
             SELECT COUNT(*)::int AS count
             FROM owner_achievements
             WHERE ${scopedWhere}
               AND name IS NOT NULL
               AND LENGTH(BTRIM(name)) > 0`,
            scopedParams
        );

        const totalNameCount = Number(nameCountRes.rows[0]?.count || 0);
        let sampleRows = [];

        if (totalNameCount > 0) {
            if (nameStrategy === 'balanced') {
                const distinctTypeCount = Math.max(byType.filter((item) => !!item.type).length, 1);
                const perTypeCap = Math.max(2, Math.ceil(nameLimit / distinctTypeCount));
                const balancedParams = [...scopedParams, perTypeCap, nameLimit];
                const balancedRes = await db.query(
                    `${ownerAchievementsCte}
                     SELECT id, name, type, owner_department AS department, achievement_date
                     FROM (
                         SELECT
                             id,
                             name,
                             type,
                             owner_department,
                             achievement_date,
                             ROW_NUMBER() OVER (
                                 PARTITION BY COALESCE(type, 'unknown')
                                 ORDER BY achievement_date DESC NULLS LAST, id DESC
                             ) AS rn
                         FROM owner_achievements
                         WHERE ${scopedWhere}
                           AND name IS NOT NULL
                           AND LENGTH(BTRIM(name)) > 0
                     ) t
                     WHERE rn <= $${scopedParams.length + 1}
                     ORDER BY achievement_date DESC NULLS LAST, id DESC
                     LIMIT $${scopedParams.length + 2}`,
                    balancedParams
                );
                sampleRows = balancedRes.rows;
            } else {
                const latestParams = [...scopedParams, nameLimit];
                const latestRes = await db.query(
                    `${ownerAchievementsCte}
                     SELECT id, name, type, owner_department AS department, achievement_date
                     FROM owner_achievements
                     WHERE ${scopedWhere}
                       AND name IS NOT NULL
                       AND LENGTH(BTRIM(name)) > 0
                     ORDER BY achievement_date DESC NULLS LAST, id DESC
                     LIMIT $${scopedParams.length + 1}`,
                    latestParams
                );
                sampleRows = latestRes.rows;
            }
        }

        const items = sampleRows
            .map((row) => ({
                name: clampText(row?.name, 120),
                type: row?.type || null,
                department: row?.department || null,
                date: toIsoDate(row?.achievement_date)
            }))
            .filter((item) => !!item.name);

        nameSamples = {
            mode,
            strategy: nameStrategy,
            sent: items.length,
            total: totalNameCount,
            truncated: items.length < totalNameCount,
            items
        };
    }

    return {
        mode,
        filters: {
            year: normalizeFilter(filters.year) || 'all',
            type: normalizeFilter(filters.type) || 'all',
            status: normalizeFilter(filters.status) || 'all',
            department: normalizeFilter(filters.department) || 'all'
        },
        overview: {
            total
        },
        byType,
        byStatus,
        byDepartment,
        byMonth,
        quality,
        nameSamples,
        protection: {
            attachmentContentSent: false,
            attachmentPathSent: false,
            attachmentFilenameSent: false,
            contributorPhoneSent: false,
            contributorNameSent: false,
            achievementNameSent: mode === 'deep',
            note: mode === 'deep'
                ? 'Deep mode sends aggregated statistics and achievement names only. No personal phone, contributor name, or attachment data is sent.'
                : 'Standard mode sends aggregated statistics only. No names, phones, or attachment data is sent.'
        }
    };
};

const buildPrompt = (question, dataset) => {
    const promptDataset = buildPromptDataset(dataset);
    const mode = dataset?.mode === 'deep' ? 'deep' : 'standard';
    const modeNote = mode === 'deep'
        ? '当前为深度分析模式：会额外提供成果名称样本（不含个人姓名与手机号）。'
        : '当前为标准分析模式：仅提供聚合统计数据，不提供成果名称。';
    const systemPrompt = `
你是科研成果统计分析助手。只能基于输入数据给出结论，不得编造事实。
要求：
1) 明确统计口径与边界；
2) 关键判断尽量给出数量/占比/趋势证据；
3) 证据不足时直接说明“数据不足以支持该结论”；
4) 建议必须可执行、可落地。
请使用中文 Markdown 输出，结构如下：
## 执行摘要
## 关键发现
## 风险与数据质量
## 优先行动（表格）
## 一句话结论
`.trim();

    const userPrompt = `
用户问题：${question || '请给出整体分析、风险和改进建议。'}

模式说明：${modeNote}

隐私口径：
- 不包含附件内容、文件路径、文件名；
- 不包含个人手机号、贡献者姓名；
- 部门统计已排除院领导部门，每条成果仅计入一个归属部门。

请先结论后证据再建议。以下是可用数据 JSON：
${JSON.stringify(promptDataset)}
`.trim();

    return {
        systemPrompt,
        userPrompt
    };
};

const callProvider = async (provider, config, systemPrompt, userPrompt, maxTokens = AI_MAX_TOKENS) => {
    const endpoint = `${config.baseUrl.replace(/\/+$/, '')}/chat/completions`;
    const aiResp = await postJson(
        endpoint,
        {
            model: config.model,
            temperature: 0.2,
            max_tokens: maxTokens,
            messages: [
                { role: 'system', content: systemPrompt },
                { role: 'user', content: userPrompt }
            ]
        },
        {
            Authorization: `Bearer ${config.apiKey}`
        },
        AI_TIMEOUT_MS
    );

    const analysis = aiResp?.choices?.[0]?.message?.content || '';
    if (!analysis) {
        throw new Error('provider returned empty analysis');
    }
    return {
        provider,
        model: config.model,
        analysis
    };
};

router.post('/achievements', verifyToken, async (req, res) => {
    if (!ADMIN_ROLES.has(req.user.role)) {
        return res.status(403).json({ message: 'No permission to use AI analysis.' });
    }

    const requestedProvider = normalizeProvider(req.body?.provider || 'auto');
    const providerConfigs = getProviderConfigs();
    const availableProviders = getAvailableProviders(providerConfigs);
    if (availableProviders.length === 0) {
        return res.status(503).json({ message: 'AI service is not configured. Please configure DeepSeek API settings.' });
    }

    const providerOrder = resolveProviderOrder(requestedProvider, availableProviders);
    if (providerOrder.length === 0) {
        return res.status(503).json({
            message: `Requested provider '${requestedProvider}' is unavailable. Available providers: ${availableProviders.join(', ')}`
        });
    }

    const question = clampText(req.body?.question, 600);
    const filters = req.body?.filters || {};
    const mode = normalizeMode(req.body?.mode);
    const nameLimit = normalizeNameLimit(req.body?.nameLimit);
    const nameStrategy = normalizeNameStrategy(req.body?.nameStrategy);
    const analysisOptions = { mode, nameLimit, nameStrategy };
    const clientIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;

    try {
        const dataset = await gatherAnalysisDataset(filters, analysisOptions);
        const { systemPrompt, userPrompt } = buildPrompt(question, dataset);
        const triedProviders = [];
        const errors = [];
        let aiResult = null;

        for (const provider of providerOrder) {
            triedProviders.push(provider);
            try {
                aiResult = await callProvider(provider, providerConfigs[provider], systemPrompt, userPrompt, AI_MAX_TOKENS);
                break;
            } catch (providerErr) {
                errors.push(`${provider}: ${providerErr.message}`);
            }
        }

        if (!aiResult) {
            throw new Error(`All AI providers failed: ${errors.join(' | ')}`);
        }

        logger.info(
            `AI analysis generated by user ${req.user.username} (role=${req.user.role}, provider=${aiResult.provider}, ip=${clientIp})`
        );

        res.json({
            provider: aiResult.provider,
            model: aiResult.model,
            generatedAt: new Date().toISOString(),
            analysis: aiResult.analysis,
            triedProviders,
            mode: dataset.mode,
            nameSamples: dataset.nameSamples,
            datasetSummary: {
                filters: dataset.filters,
                total: dataset.overview.total,
                byTypeCount: dataset.byType.length,
                byDepartmentCount: dataset.byDepartment.length,
                byMonthCount: dataset.byMonth.length
            },
            protection: dataset.protection
        });
    } catch (err) {
        logger.error(`AI analysis failed for user ${req.user.username}: ${err.message}`);
        res.status(500).json({ message: `AI analysis failed: ${err.message}` });
    }
});

router.post('/achievements/stream', verifyToken, async (req, res) => {
    if (!ADMIN_ROLES.has(req.user.role)) {
        return res.status(403).json({ message: 'No permission to use AI analysis.' });
    }

    const requestedProvider = normalizeProvider(req.body?.provider || 'auto');
    const providerConfigs = getProviderConfigs();
    const availableProviders = getAvailableProviders(providerConfigs);
    if (availableProviders.length === 0) {
        return res.status(503).json({ message: 'AI service is not configured. Please configure DeepSeek API settings.' });
    }

    const providerOrder = resolveProviderOrder(requestedProvider, availableProviders);
    if (providerOrder.length === 0) {
        return res.status(503).json({
            message: `Requested provider '${requestedProvider}' is unavailable. Available providers: ${availableProviders.join(', ')}`
        });
    }

    const question = clampText(req.body?.question, 600);
    const filters = req.body?.filters || {};
    const mode = normalizeMode(req.body?.mode);
    const nameLimit = normalizeNameLimit(req.body?.nameLimit);
    const nameStrategy = normalizeNameStrategy(req.body?.nameStrategy);
    const analysisOptions = { mode, nameLimit, nameStrategy };
    const clientIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;

    try {
        const dataset = await gatherAnalysisDataset(filters, analysisOptions);
        const { systemPrompt, userPrompt } = buildPrompt(question, dataset);
        const triedProviders = [];
        const errors = [];

        res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
        res.setHeader('Cache-Control', 'no-cache, no-transform');
        res.setHeader('Connection', 'keep-alive');
        res.setHeader('X-Accel-Buffering', 'no');
        if (typeof res.flushHeaders === 'function') {
            res.flushHeaders();
        }

        let streamResult = null;
        for (const provider of providerOrder) {
            triedProviders.push(provider);
            try {
                streamResult = await streamProviderToClient({
                    req,
                    res,
                    provider,
                    config: providerConfigs[provider],
                    systemPrompt,
                    userPrompt,
                    maxTokens: AI_MAX_TOKENS,
                    timeoutMs: AI_TIMEOUT_MS
                });
                break;
            } catch (providerErr) {
                errors.push(`${provider}: ${providerErr.message}`);
                writeSse(res, 'provider_error', {
                    provider,
                    message: providerErr.message
                });
            }
        }

        if (!streamResult) {
            writeSse(res, 'error', { message: `All AI providers failed: ${errors.join(' | ')}` });
            writeSse(res, 'done', { success: false, triedProviders });
            res.end();
            return;
        }

        logger.info(
            `AI stream analysis generated by user ${req.user.username} (role=${req.user.role}, provider=${streamResult.provider}, ip=${clientIp})`
        );

        writeSse(res, 'done', {
            success: true,
            provider: streamResult.provider,
            model: streamResult.model,
            generatedAt: new Date().toISOString(),
            triedProviders,
            mode: dataset.mode,
            nameSamples: dataset.nameSamples,
            datasetSummary: {
                filters: dataset.filters,
                total: dataset.overview.total,
                byTypeCount: dataset.byType.length,
                byDepartmentCount: dataset.byDepartment.length,
                byMonthCount: dataset.byMonth.length
            },
            protection: dataset.protection
        });
        res.end();
    } catch (err) {
        logger.error(`AI stream analysis failed for user ${req.user.username}: ${err.message}`);
        if (res.headersSent) {
            writeSse(res, 'error', { message: `AI analysis failed: ${err.message}` });
            writeSse(res, 'done', { success: false });
            res.end();
            return;
        }
        res.status(500).json({ message: `AI analysis failed: ${err.message}` });
    }
});

module.exports = router;


