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

const prisma = new PrismaClient();

const createZoneSchema = z.object({
    name: z.string().min(2),
    code: z.string().min(2).max(10),
    description: z.string().optional(),
    escortRequired: z.boolean().default(false),
    badgeRequired: z.boolean().default(true),
    ndaRequired: z.boolean().default(false),
    allowedTypes: z.array(z.enum(['VISITOR', 'CONTRACTOR', 'INSPECTOR', 'VENDOR', 'DELIVERY', 'OTHER'])).optional(),
    maxOccupancy: z.number().int().positive().optional(),
    parentZoneId: z.string().uuid().optional()
});

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

    /**
     * GET /zones
     * List all access zones
     */
    fastify.get('/', {
        handler: async (request, reply) => {
            try {
                const zones = await prisma.accessZone.findMany({
                    include: {
                        parentZone: { select: { id: true, name: true, code: true } },
                        childZones: { select: { id: true, name: true, code: true } }
                    },
                    orderBy: { name: 'asc' }
                });

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

    /**
     * POST /zones
     * Create new access zone
     */
    fastify.post('/', {
        handler: async (request, reply) => {
            try {
                const data = createZoneSchema.parse(request.body);

                const zone = await prisma.accessZone.create({
                    data: {
                        ...data,
                        allowedTypes: data.allowedTypes || []
                    }
                });

                return zone;
            } catch (error: any) {
                if (error.code === 'P2002') {
                    return reply.status(400).send({ error: 'Zone code already exists' });
                }
                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 zone' });
            }
        }
    });

    /**
     * GET /zones/:id
     * Get zone details with hierarchy
     */
    fastify.get('/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;

                const zone = await prisma.accessZone.findUnique({
                    where: { id },
                    include: {
                        parentZone: true,
                        childZones: true
                    }
                });

                if (!zone) {
                    return reply.status(404).send({ error: 'Zone not found' });
                }

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

    /**
     * PUT /zones/:id
     * Update access zone
     */
    fastify.put('/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const data = createZoneSchema.partial().parse(request.body);

                const zone = await prisma.accessZone.update({
                    where: { id },
                    data
                });

                return zone;
            } catch (error: any) {
                if (error.code === 'P2025') {
                    return reply.status(404).send({ error: 'Zone not found' });
                }
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to update zone' });
            }
        }
    });

    /**
     * DELETE /zones/:id
     * Delete access zone
     */
    fastify.delete('/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;

                // Check for child zones
                const childCount = await prisma.accessZone.count({
                    where: { parentZoneId: id }
                });

                if (childCount > 0) {
                    return reply.status(400).send({
                        error: 'Cannot delete zone with child zones',
                        childCount
                    });
                }

                await prisma.accessZone.delete({ where: { id } });

                return { success: true };
            } catch (error: any) {
                if (error.code === 'P2025') {
                    return reply.status(404).send({ error: 'Zone not found' });
                }
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to delete zone' });
            }
        }
    });

    /**
     * GET /zones/:id/occupancy
     * Get current occupancy of a zone
     */
    fastify.get('/:id/occupancy', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;

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

                // Count visitors currently in this zone
                const activeVisitors = await prisma.visitorLog.findMany({
                    where: {
                        status: 'CHECKED_IN',
                        exitTime: null,
                        zones: { has: id }
                    },
                    include: {
                        visitor: {
                            select: { id: true, name: true, company: true, type: true }
                        }
                    }
                });

                return {
                    zone: { id: zone.id, name: zone.name, code: zone.code },
                    maxOccupancy: zone.maxOccupancy,
                    currentOccupancy: activeVisitors.length,
                    atCapacity: zone.maxOccupancy ? activeVisitors.length >= zone.maxOccupancy : false,
                    visitors: activeVisitors.map(log => ({
                        logId: log.id,
                        ...log.visitor,
                        entryTime: log.entryTime
                    }))
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to get zone occupancy' });
            }
        }
    });

    /**
     * POST /zones/:id/log-entry
     * Log a visitor entering a zone
     */
    fastify.post('/:id/log-entry', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { visitorLogId } = request.body as any;

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

                const log = await prisma.visitorLog.findUnique({
                    where: { id: visitorLogId },
                    include: { visitor: true }
                });

                if (!log) {
                    return reply.status(404).send({ error: 'Visitor log not found' });
                }

                if (log.status !== 'CHECKED_IN') {
                    return reply.status(400).send({ error: 'Visitor not currently checked in' });
                }

                // Check if visitor type is allowed
                if (zone.allowedTypes.length > 0 && !zone.allowedTypes.includes(log.visitor.type)) {
                    return reply.status(403).send({
                        error: 'Visitor type not allowed in this zone',
                        allowed: zone.allowedTypes,
                        visitorType: log.visitor.type
                    });
                }

                // Check NDA requirement
                if (zone.ndaRequired && !log.visitor.ndaAccepted) {
                    return reply.status(403).send({ error: 'NDA required for this zone' });
                }

                // Check escort requirement
                if (zone.escortRequired && !log.escortId) {
                    return reply.status(403).send({ error: 'Escort required for this zone' });
                }

                // Check capacity
                if (zone.maxOccupancy) {
                    const count = await prisma.visitorLog.count({
                        where: {
                            status: 'CHECKED_IN',
                            exitTime: null,
                            zones: { has: id }
                        }
                    });
                    if (count >= zone.maxOccupancy) {
                        return reply.status(403).send({
                            error: 'Zone at maximum capacity',
                            maxOccupancy: zone.maxOccupancy
                        });
                    }
                }

                // Add zone to visitor's zones array
                const updatedLog = await prisma.visitorLog.update({
                    where: { id: visitorLogId },
                    data: {
                        zones: [...new Set([...log.zones, id])]
                    }
                });

                return {
                    success: true,
                    zone: { id: zone.id, name: zone.name, code: zone.code },
                    zonesVisited: updatedLog.zones.length
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to log zone entry' });
            }
        }
    });
}
