import { Request, Response } from 'express';
import * as applicationRecordService from '../services/applicationRecordService.js';
import * as paintingSchemeService from '../services/paintingSchemeService.js';
import * as inspectionService from '../services/inspectionService.js';

interface AnalysisResult {
    pieceDescription: string;
    schemeName: string;
    schemeType: string;

    // Yield (Rendimento)
    realYield: number;
    theoreticalYield: number;
    yieldVariance: number; // percentage
    yieldStatus: 'approved' | 'warning' | 'critical';

    // Dilution
    diluentUsed: number;
    volumeUsed: number;
    realDilution: number;
    targetDilution: number;
    dilutionStatus: 'approved' | 'warning' | 'critical';

    // Thickness (Espessura)
    realDFT: number; // Average of dryThicknessCalc or inspection points
    minDFT: number;
    maxDFT: number;
    dftStatus: 'approved' | 'warning' | 'critical';

    notes: string[];
}

export const getProjectAnalysis = async (req: Request, res: Response) => {
    try {
        const { projectId } = req.params;

        const records = await applicationRecordService.getApplicationRecordsByProject(projectId as string);
        const schemes = await paintingSchemeService.getPaintingSchemesByProject(projectId as string);
        const inspections = await inspectionService.getInspectionsByProject(projectId as string);

        const analysis: AnalysisResult[] = [];

        // Group records by pieceDescription to handle multiple layers/records for same piece if needed
        // For simple MVP, assuming 1 record per piece for calculation or analyzing individual records

        for (const record of records) {
            // Find scheme used (assuming application record doesn't store schemeId directly, we might need to match by name or context? 
            // Checking types.ts, ApplicationRecord doesn't have schemeId. It has 'coatStage'. 
            // PaintingScheme has 'type' which might match 'coatStage' or project context. 
            // Without direct link, we might have to infer or maybe the user links them by name/description effectively?
            // Actually, in many cases there's only one scheme active per project or we check if schemes exist.
            // Let's try to match if possible, or use a default if only one scheme exists.

            // Note: In a real robust system we'd link Record -> Scheme. 
            // Here we will try to find a scheme that matches the coatStage or just take the first one if length is 1.
            const scheme = schemes.find(s => s.type === record.coatStage) || schemes[0];

            if (!scheme) continue;

            // Calculations

            // 1. Yield
            // Yield = Area / Volume
            const realYield = (record.areaPainted && record.volumeUsed)
                ? (record.areaPainted / record.volumeUsed)
                : 0;

            const theoreticalYield = scheme.yieldTheoretical || 0;

            const yieldData = calculateVariance(realYield, theoreticalYield);

            // 2. Dilution
            const diluent = record.diluentUsed || 0;
            const volume = record.volumeUsed || 1; // avoid div by zero
            const realDilution = (diluent / volume) * 100;
            const targetDilution = scheme.dilution || 0;

            const dilutionData = calculateVariance(realDilution, targetDilution); // true for "lower is not always better/worse", requires exact match? 
            // Actually for dilution, usually there's a limit. Let's assume variance check.

            // 3. DFT (Dry Film Thickness)
            // Use inspection data if available for this piece, otherwise calculated
            const pieceInspection = inspections.find(i => i.pieceDescription === record.pieceDescription);

            let realDFT = 0;
            if (pieceInspection) {
                const points = (pieceInspection.epsPoints || []).filter((p: number | null | undefined): p is number => p !== undefined && p !== null);
                if (points.length > 0) {
                    realDFT = points.reduce((a: number, b: number) => a + b, 0) / points.length;
                }
            } else {
                realDFT = record.dryThicknessCalc || 0;
            }

            const minDFT = scheme.epsMin || 0;
            const maxDFT = scheme.epsMax || 9999;

            let dftStatus: 'approved' | 'warning' | 'critical' = 'approved';
            if (realDFT < minDFT) dftStatus = 'critical';
            else if (realDFT > maxDFT) dftStatus = 'warning';


            analysis.push({
                pieceDescription: record.pieceDescription || 'Desconhecido',
                schemeName: scheme.name,
                schemeType: scheme.type || '',

                realYield: Number(realYield.toFixed(2)),
                theoreticalYield: Number(theoreticalYield.toFixed(2)),
                yieldVariance: Number(yieldData.variance.toFixed(2)),
                yieldStatus: yieldData.status,

                diluentUsed: diluent,
                volumeUsed: volume,
                realDilution: Number(realDilution.toFixed(1)),
                targetDilution: Number(targetDilution.toFixed(1)),
                dilutionStatus: dilutionData.status, // We might want to warn if dilution is > target

                realDFT: Number(realDFT.toFixed(1)),
                minDFT: minDFT,
                maxDFT: maxDFT,
                dftStatus,

                notes: []
            });
        }

        res.json(analysis);

    } catch (error: unknown) {
        console.error('CRITICAL: Error in getProjectAnalysis controller:', error);
        const message = error instanceof Error ? error.message : 'Unknown error';
        res.status(500).json({ 
            error: message,
            stack: error instanceof Error ? error.stack : undefined 
        });
    }
};

function calculateVariance(actual: number, target: number): { variance: number, status: 'approved' | 'warning' | 'critical' } {
    if (!target) return { variance: 0, status: 'approved' };

    // Variance % = ((Actual - Target) / Target) * 100
    const variance = ((actual - target) / target) * 100;

    // Rules (Simplified)
    // Yield: Higher is usually good (efficient), but too high might mean too thin. 
    // Lower means using more paint -> expensive.

    let status: 'approved' | 'warning' | 'critical' = 'approved';

    // If deviation is > 20% (warning) or > 30% (critical)
    if (Math.abs(variance) > 30) status = 'critical';
    else if (Math.abs(variance) > 20) status = 'warning';

    return { variance, status };
}
