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

const prisma = new PrismaClient();

const createAnnouncementSchema = z.object({
    title: z.string().min(3),
    body: z.string(),
    priority: z.enum(['INFO', 'WARNING', 'CRITICAL']).default('INFO'),
    requiresAck: z.boolean().default(false),
    targetRoles: z.array(z.string()).optional(),
    expiresAt: z.string().datetime().optional()
});

const createShiftNoteSchema = z.object({
    content: z.string().min(1),
    roomId: z.string().uuid().optional(),
    batchId: z.string().uuid().optional(),
    importance: z.enum(['LOW', 'NORMAL', 'HIGH', 'URGENT']).default('NORMAL'),
    expiresAt: z.string().datetime().optional()
});

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

    // ==================== ANNOUNCEMENTS ====================

    /**
     * GET /announcements
     * Get all active announcements for the current user
     */
    fastify.get('/announcements', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;
                const userRole = user.role;

                const announcements = await prisma.announcement.findMany({
                    where: {
                        AND: [
                            {
                                OR: [
                                    { targetRoles: { isEmpty: true } }, // No targeting = everyone
                                    { targetRoles: { has: userRole } }
                                ]
                            },
                            {
                                OR: [
                                    { expiresAt: null },
                                    { expiresAt: { gt: new Date() } }
                                ]
                            }
                        ]
                    },
                    include: {
                        createdBy: { select: { id: true, name: true } },
                        acks: {
                            where: { userId: user.id }
                        }
                    },
                    orderBy: [
                        { priority: 'desc' },
                        { createdAt: 'desc' }
                    ]
                });

                return announcements.map(a => ({
                    ...a,
                    isRead: a.acks[0]?.readAt != null,
                    isAcknowledged: a.acks[0]?.acknowledgedAt != null
                }));
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch announcements' });
            }
        }
    });

    /**
     * GET /announcements/pending
     * Get announcements requiring acknowledgement that user hasn't acknowledged
     */
    fastify.get('/announcements/pending', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;

                const pending = await prisma.announcement.findMany({
                    where: {
                        requiresAck: true,
                        OR: [
                            { expiresAt: null },
                            { expiresAt: { gt: new Date() } }
                        ],
                        NOT: {
                            acks: {
                                some: {
                                    userId: user.id,
                                    acknowledgedAt: { not: null }
                                }
                            }
                        }
                    },
                    include: {
                        createdBy: { select: { id: true, name: true } }
                    },
                    orderBy: { priority: 'desc' }
                });

                return {
                    count: pending.length,
                    announcements: pending
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch pending announcements' });
            }
        }
    });

    /**
     * POST /announcements
     * Create new announcement (managers/owners only)
     */
    fastify.post('/announcements', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;
                const data = createAnnouncementSchema.parse(request.body);

                const announcement = await prisma.announcement.create({
                    data: {
                        ...data,
                        expiresAt: data.expiresAt ? new Date(data.expiresAt) : null,
                        targetRoles: data.targetRoles || [],
                        createdById: user.id
                    },
                    include: {
                        createdBy: { select: { id: true, name: true } }
                    }
                });

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

    /**
     * POST /announcements/:id/read
     * Mark announcement as read
     */
    fastify.post('/announcements/:id/read', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

                await prisma.announcementAck.upsert({
                    where: {
                        announcementId_userId: {
                            announcementId: id,
                            userId: user.id
                        }
                    },
                    create: {
                        announcementId: id,
                        userId: user.id,
                        readAt: new Date()
                    },
                    update: {
                        readAt: new Date()
                    }
                });

                return { success: true };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to mark as read' });
            }
        }
    });

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

                await prisma.announcementAck.upsert({
                    where: {
                        announcementId_userId: {
                            announcementId: id,
                            userId: user.id
                        }
                    },
                    create: {
                        announcementId: id,
                        userId: user.id,
                        readAt: new Date(),
                        acknowledgedAt: new Date()
                    },
                    update: {
                        acknowledgedAt: new Date()
                    }
                });

                return { success: true, acknowledgedAt: new Date().toISOString() };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to acknowledge' });
            }
        }
    });

    /**
     * GET /announcements/:id/acks
     * Get acknowledgement status for an announcement (admin)
     */
    fastify.get('/announcements/:id/acks', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;

                const announcement = await prisma.announcement.findUnique({
                    where: { id },
                    include: {
                        acks: {
                            include: {
                                user: { select: { id: true, name: true, email: true } }
                            }
                        }
                    }
                });

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

                // Get all users who should have acknowledged
                const targetUsers = announcement.targetRoles.length > 0
                    ? await prisma.user.findMany({
                        where: { role: { in: announcement.targetRoles as any } },
                        select: { id: true, name: true, email: true }
                    })
                    : await prisma.user.findMany({
                        select: { id: true, name: true, email: true }
                    });

                const ackedUserIds = new Set(
                    announcement.acks
                        .filter(a => a.acknowledgedAt)
                        .map(a => a.userId)
                );

                return {
                    announcement: { id: announcement.id, title: announcement.title },
                    requiresAck: announcement.requiresAck,
                    totalUsers: targetUsers.length,
                    acknowledged: announcement.acks.filter(a => a.acknowledgedAt).length,
                    pending: targetUsers.filter(u => !ackedUserIds.has(u.id)),
                    acks: announcement.acks
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch acknowledgements' });
            }
        }
    });

    // ==================== SHIFT NOTES ====================

    /**
     * GET /shift-notes
     * Get recent shift notes
     */
    fastify.get('/shift-notes', {
        handler: async (request, reply) => {
            try {
                const { roomId, batchId, limit = 20 } = request.query as any;

                const where: any = {
                    OR: [
                        { expiresAt: null },
                        { expiresAt: { gt: new Date() } }
                    ]
                };

                if (roomId) where.roomId = roomId;
                if (batchId) where.batchId = batchId;

                const notes = await prisma.shiftNote.findMany({
                    where,
                    include: {
                        createdBy: { select: { id: true, name: true } }
                    },
                    orderBy: { createdAt: 'desc' },
                    take: parseInt(limit)
                });

                const user = request.user as any;
                return notes.map(n => ({
                    ...n,
                    isRead: n.readBy.includes(user.id)
                }));
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch shift notes' });
            }
        }
    });

    /**
     * POST /shift-notes
     * Create a shift note
     */
    fastify.post('/shift-notes', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;
                const data = createShiftNoteSchema.parse(request.body);

                const note = await prisma.shiftNote.create({
                    data: {
                        ...data,
                        expiresAt: data.expiresAt ? new Date(data.expiresAt) : null,
                        createdById: user.id,
                        readBy: [user.id] // Creator has read it
                    },
                    include: {
                        createdBy: { select: { id: true, name: true } }
                    }
                });

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

    /**
     * POST /shift-notes/:id/read
     * Mark shift note as read
     */
    fastify.post('/shift-notes/:id/read', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

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

                if (!note.readBy.includes(user.id)) {
                    await prisma.shiftNote.update({
                        where: { id },
                        data: {
                            readBy: [...note.readBy, user.id]
                        }
                    });
                }

                return { success: true };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to mark as read' });
            }
        }
    });
}
