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

const prisma = new PrismaClient();

// Validation schemas
const createVisitorSchema = z.object({
    name: z.string().min(2),
    company: z.string().optional(),
    email: z.string().email().optional(),
    phone: z.string().optional(),
    photoUrl: z.string().url().optional(),
    idType: z.string().optional(),
    idNumber: z.string().optional(),
    type: z.enum(['VISITOR', 'CONTRACTOR', 'INSPECTOR', 'VENDOR', 'DELIVERY', 'OTHER']).default('VISITOR'),
    purpose: z.string(),
    notes: z.string().optional()
});

const checkInSchema = z.object({
    escortId: z.string().uuid().optional(),
    zones: z.array(z.string()).optional(),
    temperatureF: z.number().optional(),
    signature: z.string().optional(),
    ndaAccepted: z.boolean().optional(),
    notes: z.string().optional()
});

export async function visitorRoutes(fastify: FastifyInstance) {
    // Note: Most visitor routes are public to support the Kiosk mode.
    // Specific admin routes (Revoke, Report) are protected inside their handlers.

    /**
     * GET /visitors
     * List all visitors with optional filters
     */
    fastify.get('/', {
        handler: async (request, reply) => {
            try {
                const { status, type, search, page = 1, limit = 50 } = request.query as any;

                const where: any = {};
                if (type) where.type = type;
                if (search) {
                    where.OR = [
                        { name: { contains: search, mode: 'insensitive' } },
                        { company: { contains: search, mode: 'insensitive' } },
                        { email: { contains: search, mode: 'insensitive' } }
                    ];
                }

                const visitors = await prisma.visitor.findMany({
                    where,
                    include: {
                        logs: {
                            orderBy: { entryTime: 'desc' },
                            take: 1
                        }
                    },
                    orderBy: { createdAt: 'desc' },
                    skip: (page - 1) * limit,
                    take: limit
                });

                const total = await prisma.visitor.count({ where });

                return {
                    visitors,
                    pagination: {
                        page,
                        limit,
                        total,
                        pages: Math.ceil(total / limit)
                    }
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch visitors' });
            }
        }
    });

    /**
     * GET /visitors/active
     * Get currently checked-in visitors
     */
    fastify.get('/active', {
        handler: async (request, reply) => {
            try {
                const activeVisitors = await prisma.visitorLog.findMany({
                    where: {
                        status: 'CHECKED_IN',
                        exitTime: null
                    },
                    include: {
                        visitor: true,
                        escort: {
                            select: { id: true, name: true }
                        }
                    },
                    orderBy: { entryTime: 'desc' }
                });

                return {
                    count: activeVisitors.length,
                    visitors: activeVisitors.map(log => ({
                        logId: log.id,
                        visitorId: log.visitor.id,
                        name: log.visitor.name,
                        company: log.visitor.company,
                        type: log.visitor.type,
                        purpose: log.visitor.purpose,
                        entryTime: log.entryTime,
                        escort: log.escort,
                        badgeNumber: log.badgeNumber,
                        zones: log.zones
                    }))
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch active visitors' });
            }
        }
    });

    /**
     * POST /visitors
     * Create new visitor (pre-register)
     */
    fastify.post('/', {
        handler: async (request, reply) => {
            try {
                const data = createVisitorSchema.parse(request.body);

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

                return visitor;
            } 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 visitor' });
            }
        }
    });

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

                const visitor = await prisma.visitor.findUnique({
                    where: { id },
                    include: {
                        logs: {
                            include: {
                                escort: { select: { id: true, name: true } },
                                approvedBy: { select: { id: true, name: true } }
                            },
                            orderBy: { entryTime: 'desc' }
                        }
                    }
                });

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

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

    /**
     * POST /visitors/:id/check-in
     * Check in a visitor
     */
    fastify.post('/:id/check-in', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const data = checkInSchema.parse(request.body);
                const userId = (request.user as any)?.id;

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

                // Check if already checked in
                const existingLog = await prisma.visitorLog.findFirst({
                    where: {
                        visitorId: id,
                        status: 'CHECKED_IN',
                        exitTime: null
                    }
                });

                if (existingLog) {
                    return reply.status(400).send({ error: 'Visitor already checked in' });
                }

                // Generate badge number
                const today = new Date().toISOString().split('T')[0].replace(/-/g, '');
                const count = await prisma.visitorLog.count({
                    where: {
                        entryTime: {
                            gte: new Date(new Date().setHours(0, 0, 0, 0))
                        }
                    }
                });
                const badgeNumber = `V-${today}-${String(count + 1).padStart(3, '0')}`;

                // Update NDA if accepted
                if (data.ndaAccepted) {
                    await prisma.visitor.update({
                        where: { id },
                        data: {
                            ndaAccepted: true,
                            ndaAcceptedAt: new Date()
                        }
                    });
                }

                const log = await prisma.visitorLog.create({
                    data: {
                        visitorId: id,
                        status: 'CHECKED_IN',
                        escortId: data.escortId,
                        approvedById: userId,
                        badgeNumber,
                        badgeExpiry: new Date(Date.now() + 12 * 60 * 60 * 1000), // 12 hours
                        zones: data.zones || [],
                        signature: data.signature,
                        temperatureF: data.temperatureF,
                        notes: data.notes
                    },
                    include: {
                        visitor: true,
                        escort: { select: { id: true, name: true } }
                    }
                });

                return {
                    success: true,
                    badgeNumber,
                    visitId: log.id,
                    log
                };
            } 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 check in visitor' });
            }
        }
    });

    /**
     * POST /visitors/:id/check-out
     * Check out a visitor
     */
    fastify.post('/:id/check-out', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { notes } = request.body as any;

                const log = await prisma.visitorLog.findFirst({
                    where: {
                        visitorId: id,
                        status: 'CHECKED_IN',
                        exitTime: null
                    }
                });

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

                const updatedLog = await prisma.visitorLog.update({
                    where: { id: log.id },
                    data: {
                        status: 'CHECKED_OUT',
                        exitTime: new Date(),
                        notes: notes ? `${log.notes || ''}\nCheckout: ${notes}`.trim() : log.notes
                    },
                    include: {
                        visitor: true
                    }
                });

                return {
                    success: true,
                    duration: Math.round((updatedLog.exitTime!.getTime() - updatedLog.entryTime.getTime()) / 60000),
                    log: updatedLog
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to check out visitor' });
            }
        }
    });

    /**
     * POST /visitors/:id/revoke
     * Revoke visitor access immediately
     */
    fastify.post('/:id/revoke', {
        handler: async (request, reply) => {
            try {
                await request.jwtVerify();
                const { id } = request.params as any;
                const { notes } = request.body as any;
                const userId = (request.user as any)?.id;

                const log = await prisma.visitorLog.findFirst({
                    where: {
                        visitorId: id,
                        status: 'CHECKED_IN',
                        exitTime: null
                    }
                });

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

                const updatedLog = await prisma.visitorLog.update({
                    where: { id: log.id },
                    data: {
                        status: 'REVOKED',
                        exitTime: new Date(),
                        notes: `ACCESS REVOKED by User ${userId}. ${notes || ''}`.trim()
                    },
                    include: {
                        visitor: true
                    }
                });

                return {
                    success: true,
                    message: 'Access revoked successfully',
                    log: updatedLog
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to revoke access' });
            }
        }
    });

    /**
     * GET /visitors/report
     * Generate visitor report for compliance
     */
    fastify.get('/report', {
        handler: async (request, reply) => {
            try {
                await request.jwtVerify();
                const { startDate, endDate, type } = request.query as any;

                const where: any = {
                    entryTime: {
                        gte: startDate ? new Date(startDate) : new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
                        lte: endDate ? new Date(endDate) : new Date()
                    }
                };

                if (type) {
                    where.visitor = { type };
                }

                const logs = await prisma.visitorLog.findMany({
                    where,
                    include: {
                        visitor: true,
                        escort: { select: { id: true, name: true } },
                        approvedBy: { select: { id: true, name: true } }
                    },
                    orderBy: { entryTime: 'desc' }
                });

                // Group by date
                const byDate: Record<string, any[]> = {};
                logs.forEach(log => {
                    const date = log.entryTime.toISOString().split('T')[0];
                    if (!byDate[date]) byDate[date] = [];
                    byDate[date].push(log);
                });

                return {
                    generatedAt: new Date().toISOString(),
                    dateRange: {
                        start: where.entryTime.gte.toISOString(),
                        end: where.entryTime.lte.toISOString()
                    },
                    summary: {
                        totalVisits: logs.length,
                        uniqueVisitors: new Set(logs.map(l => l.visitorId)).size,
                        byType: logs.reduce((acc: any, l) => {
                            acc[l.visitor.type] = (acc[l.visitor.type] || 0) + 1;
                            return acc;
                        }, {}),
                        averageDurationMinutes: logs
                            .filter(l => l.exitTime)
                            .reduce((sum, l) => sum + (l.exitTime!.getTime() - l.entryTime.getTime()) / 60000, 0) /
                            (logs.filter(l => l.exitTime).length || 1)
                    },
                    byDate,
                    logs: logs.slice(0, 100)
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to generate report' });
            }
        }
    });
}
