File size: 3,350 Bytes
7e49c4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { z } from 'zod';
import db from '../utils/firebaseClient.js';
import { runSimulation } from '../services/simulationService.js';
import { AppError } from '../middleware/errorHandler.js';
import { sanitizeText } from '../utils/sanitize.js';
import crypto from 'crypto';

const simulationSchema = z.object({
  assessmentId: z.string().min(1, 'assessmentId is required'),
  scenarioName: z.string().min(1, 'scenarioName is required').max(100),
  scenarioParams: z.record(z.union([z.string(), z.number(), z.boolean()])),
});

export const createSimulation = async (req, res, next) => {
  try {
    const data = simulationSchema.parse(req.body);
    data.assessmentId = sanitizeText(data.assessmentId);
    data.scenarioName = sanitizeText(data.scenarioName);

    const assessmentDoc = await db.collection('assessments').doc(data.assessmentId).get();
    if (!assessmentDoc.exists) throw new AppError('Assessment not found', 404);

    const assessment = assessmentDoc.data();

    const assessmentData = {
      dailyCarKm: assessment.dailyCarKm,
      carFuelType: assessment.carFuelType,
      publicTransportKmPerWeek: assessment.publicTransportKmPerWeek,
      cyclingKmPerWeek: assessment.cyclingKmPerWeek,
      shortFlightsPerYear: assessment.shortFlightsPerYear,
      longFlightsPerYear: assessment.longFlightsPerYear,
      monthlyElectricityKwh: assessment.monthlyElectricityKwh,
      renewablePercentage: assessment.renewablePercentage,
      dietType: assessment.dietType,
      clothingItemsPerYear: assessment.clothingItemsPerYear,
      electronicsItemsPerYear: assessment.electronicsItemsPerYear,
    };

    const result = runSimulation(assessmentData, data.scenarioParams, data.scenarioName);

    const simId = crypto.randomUUID();
    const simulation = {
      id: simId,
      assessmentId: data.assessmentId,
      scenarioName: result.scenarioName,
      scenarioParams: result.scenarioParams,
      originalEmission: result.originalEmission,
      projectedEmission: result.projectedEmission,
      reductionPercentage: result.reductionPercentage,
      annualSavingsKg: result.annualSavingsKg,
      createdAt: new Date().toISOString(),
    };

    await db.collection('simulations').doc(simId).set(simulation);

    res.status(201).json({ success: true, data: { ...simulation, ...result } });
  } catch (err) {
    next(err);
  }
};

export const getSimulationById = async (req, res, next) => {
  try {
    const { id } = req.params;
    const sanitizedId = sanitizeText(id);

    const simulationDoc = await db.collection('simulations').doc(sanitizedId).get();
    if (!simulationDoc.exists) throw new AppError('Simulation not found', 404);

    const simulation = simulationDoc.data();

    const assessmentDoc = await db.collection('assessments').doc(simulation.assessmentId).get();
    const assessment = assessmentDoc.exists ? assessmentDoc.data() : null;

    res.json({
      success: true,
      data: {
        ...simulation,
        id: sanitizedId,
        assessment: assessment
          ? {
              id: assessment.id || simulation.assessmentId,
              totalEmission: assessment.totalEmission,
              sustainabilityScore: assessment.sustainabilityScore,
              userId: assessment.userId,
            }
          : null,
      },
    });
  } catch (err) {
    next(err);
  }
};