import { FastifyInstance } from 'fastify';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';

const prisma = new PrismaClient();

const createSensorSchema = z.object({
    name: z.string().min(2),
    type: z.enum(['TEMPERATURE', 'HUMIDITY', 'CO2', 'LIGHT_PAR', 'LIGHT_LUX', 'PH', 'EC', 'VPD', 'SOIL_MOISTURE', 'AIR_FLOW']),
    roomId: z.string().uuid().optional(),
    location: z.string().optional(),
    deviceId: z.string().optional(),
    manufacturer: z.string().optional(),
    model: z.string().optional(),
    minThreshold: z.number().optional(),
    maxThreshold: z.number().optional()
});

const readingSchema = z.object({
    value: z.number(),
    unit: z.string(),
    timestamp: z.string().datetime().optional()
});

const profileSchema = z.object({
    name: z.string().min(2),
    stage: z.string().optional(),
    roomId: z.string().uuid().optional(),
    tempMinF: z.number().optional(),
    tempMaxF: z.number().optional(),
    humidityMin: z.number().optional(),
    humidityMax: z.number().optional(),
    co2Min: z.number().optional(),
    co2Max: z.number().optional(),
    lightHours: z.number().optional(),
    vpdMin: z.number().optional(),
    vpdMax: z.number().optional(),
    isDefault: z.boolean().optional()
});

export async function environmentRoutes(fastify: FastifyInstance) {
    // Auth middleware
    fastify.addHook('onRequest', async (request) => {
        try {
            await request.jwtVerify();
        } catch (err) {
            throw err;
        }
    });

    // ==================== SENSORS ====================

    /**
     * GET /sensors
     * List all sensors
     */
    fastify.get('/sensors', {
        handler: async (request, reply) => {
            try {
                const { roomId, type, active } = request.query as any;

                const where: any = {};
                if (roomId) where.roomId = roomId;
                if (type) where.type = type;
                if (active !== undefined) where.isActive = active === 'true';

                const sensors = await prisma.sensor.findMany({
                    where,
                    include: {
                        readings: {
                            orderBy: { timestamp: 'desc' },
                            take: 1
                        },
                        _count: { select: { alerts: true } }
                    },
                    orderBy: { name: 'asc' }
                });

                return sensors.map(s => ({
                    ...s,
                    latestReading: s.readings[0] || null,
                    alertCount: s._count.alerts
                }));
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch sensors' });
            }
        }
    });

    /**
     * POST /sensors
     * Create new sensor
     */
    fastify.post('/sensors', {
        handler: async (request, reply) => {
            try {
                const data = createSensorSchema.parse(request.body);

                const sensor = await prisma.sensor.create({ data });

                return sensor;
            } catch (error: any) {
                if (error.name === 'ZodError') {
                    return reply.status(400).send({ error: 'Validation failed', details: error.errors });
                }
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create sensor' });
            }
        }
    });

    /**
     * POST /sensors/:id/readings
     * Submit sensor reading
     */
    fastify.post('/sensors/:id/readings', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const data = readingSchema.parse(request.body);

                const sensor = await prisma.sensor.findUnique({ where: { id } });
                if (!sensor) {
                    return reply.status(404).send({ error: 'Sensor not found' });
                }

                const reading = await prisma.sensorReading.create({
                    data: {
                        sensorId: id,
                        value: data.value,
                        unit: data.unit,
                        timestamp: data.timestamp ? new Date(data.timestamp) : new Date()
                    }
                });

                // Check thresholds and create alert if needed
                if (sensor.minThreshold && data.value < sensor.minThreshold) {
                    await prisma.environmentAlert.create({
                        data: {
                            sensorId: id,
                            roomId: sensor.roomId,
                            type: `${sensor.type}_LOW`,
                            severity: 'WARNING',
                            message: `${sensor.name} reading ${data.value}${data.unit} below minimum threshold ${sensor.minThreshold}${data.unit}`,
                            value: data.value,
                            threshold: sensor.minThreshold
                        }
                    });
                }

                if (sensor.maxThreshold && data.value > sensor.maxThreshold) {
                    await prisma.environmentAlert.create({
                        data: {
                            sensorId: id,
                            roomId: sensor.roomId,
                            type: `${sensor.type}_HIGH`,
                            severity: 'WARNING',
                            message: `${sensor.name} reading ${data.value}${data.unit} above maximum threshold ${sensor.maxThreshold}${data.unit}`,
                            value: data.value,
                            threshold: sensor.maxThreshold
                        }
                    });
                }

                return reading;
            } catch (error: any) {
                if (error.name === 'ZodError') {
                    return reply.status(400).send({ error: 'Validation failed', details: error.errors });
                }
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to submit reading' });
            }
        }
    });

    /**
     * GET /sensors/:id/readings
     * Get sensor readings history
     */
    fastify.get('/sensors/:id/readings', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { hours = 24, limit = 1000 } = request.query as any;

                const since = new Date(Date.now() - hours * 60 * 60 * 1000);

                const readings = await prisma.sensorReading.findMany({
                    where: {
                        sensorId: id,
                        timestamp: { gte: since }
                    },
                    orderBy: { timestamp: 'asc' },
                    take: parseInt(limit)
                });

                return {
                    sensorId: id,
                    period: { hours: parseInt(hours), since: since.toISOString() },
                    count: readings.length,
                    readings
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch readings' });
            }
        }
    });

    // ==================== ALERTS ====================

    /**
     * GET /alerts
     * Get environment alerts
     */
    fastify.get('/alerts', {
        handler: async (request, reply) => {
            try {
                const { roomId, severity, resolved, limit = 50 } = request.query as any;

                const where: any = {};
                if (roomId) where.roomId = roomId;
                if (severity) where.severity = severity;
                if (resolved !== undefined) {
                    where.resolvedAt = resolved === 'true' ? { not: null } : null;
                }

                const alerts = await prisma.environmentAlert.findMany({
                    where,
                    include: {
                        sensor: { select: { id: true, name: true, type: true } }
                    },
                    orderBy: { createdAt: 'desc' },
                    take: parseInt(limit)
                });

                return alerts;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch alerts' });
            }
        }
    });

    /**
     * POST /alerts/:id/acknowledge
     * Acknowledge an alert
     */
    fastify.post('/alerts/:id/acknowledge', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

                const alert = await prisma.environmentAlert.update({
                    where: { id },
                    data: {
                        acknowledgedAt: new Date(),
                        acknowledgedBy: user.id
                    }
                });

                return alert;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to acknowledge alert' });
            }
        }
    });

    /**
     * POST /alerts/:id/resolve
     * Resolve an alert
     */
    fastify.post('/alerts/:id/resolve', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

                const alert = await prisma.environmentAlert.update({
                    where: { id },
                    data: {
                        resolvedAt: new Date(),
                        resolvedBy: user.id
                    }
                });

                return alert;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to resolve alert' });
            }
        }
    });

    // ==================== ENVIRONMENT PROFILES ====================

    /**
     * GET /profiles
     * List environment profiles
     */
    fastify.get('/profiles', {
        handler: async (request, reply) => {
            try {
                const profiles = await prisma.environmentProfile.findMany({
                    orderBy: [{ isDefault: 'desc' }, { name: 'asc' }]
                });

                return profiles;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch profiles' });
            }
        }
    });

    /**
     * POST /profiles
     * Create environment profile
     */
    fastify.post('/profiles', {
        handler: async (request, reply) => {
            try {
                const data = profileSchema.parse(request.body);

                const profile = await prisma.environmentProfile.create({ data });

                return profile;
            } catch (error: any) {
                if (error.name === 'ZodError') {
                    return reply.status(400).send({ error: 'Validation failed', details: error.errors });
                }
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create profile' });
            }
        }
    });

    // ==================== DASHBOARD ====================

    /**
     * GET /dashboard
     * Get environment dashboard data
     */
    fastify.get('/dashboard', {
        handler: async (request, reply) => {
            try {
                const { roomId } = request.query as any;

                const where = roomId ? { roomId } : {};
                const sensorWhere = roomId ? { roomId, isActive: true } : { isActive: true };

                // Get latest readings by sensor type
                const sensors = await prisma.sensor.findMany({
                    where: sensorWhere,
                    include: {
                        readings: {
                            orderBy: { timestamp: 'desc' },
                            take: 1
                        }
                    }
                });

                // Group by type with latest value
                const byType: Record<string, { value: number; unit: string; sensor: string; timestamp: string }[]> = {};
                sensors.forEach(s => {
                    if (s.readings[0]) {
                        if (!byType[s.type]) byType[s.type] = [];
                        byType[s.type].push({
                            value: s.readings[0].value,
                            unit: s.readings[0].unit,
                            sensor: s.name,
                            timestamp: s.readings[0].timestamp.toISOString()
                        });
                    }
                });

                // Calculate averages
                const averages: Record<string, number> = {};
                Object.entries(byType).forEach(([type, readings]) => {
                    averages[type] = readings.reduce((sum, r) => sum + r.value, 0) / readings.length;
                });

                // Get active alerts
                const activeAlerts = await prisma.environmentAlert.findMany({
                    where: {
                        ...where,
                        resolvedAt: null
                    },
                    orderBy: { createdAt: 'desc' },
                    take: 10
                });

                // Get active profile
                const profile = roomId
                    ? await prisma.environmentProfile.findFirst({ where: { roomId } })
                    : await prisma.environmentProfile.findFirst({ where: { isDefault: true } });

                return {
                    sensors: sensors.length,
                    readings: byType,
                    averages,
                    alerts: {
                        active: activeAlerts.length,
                        list: activeAlerts
                    },
                    profile,
                    timestamp: new Date().toISOString()
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch dashboard' });
            }
        }
    });
}
