import { FastifyInstance } from 'fastify';
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';
import { createAuditLog } from './audit.routes';

const prisma = new PrismaClient();

const createDocumentSchema = z.object({
    title: z.string().min(3),
    type: z.enum(['SOP', 'POLICY', 'TRAINING', 'FORM', 'CHECKLIST', 'OTHER']),
    content: z.string(),
    requiresAck: z.boolean().default(false),
    targetRoles: z.array(z.string()).optional(),
    effectiveDate: z.string().datetime().optional(),
    expiresAt: z.string().datetime().optional()
});

const updateDocumentSchema = createDocumentSchema.partial().extend({
    changeNotes: z.string().optional()
});

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

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

                const where: any = {};
                if (type) where.type = type;
                if (status) where.status = status;
                if (search) {
                    where.title = { contains: search, mode: 'insensitive' };
                }

                const documents = await prisma.document.findMany({
                    where,
                    include: {
                        createdBy: { select: { id: true, name: true } },
                        approvedBy: { select: { id: true, name: true } },
                        _count: { select: { acks: true } }
                    },
                    orderBy: [{ status: 'asc' }, { updatedAt: 'desc' }]
                });

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

    /**
     * GET /documents/library
     * Get approved documents organized by type (SOP library)
     */
    fastify.get('/library', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;

                const documents = await prisma.document.findMany({
                    where: { status: 'APPROVED' },
                    include: {
                        createdBy: { select: { id: true, name: true } },
                        acks: { where: { userId: user.id } }
                    },
                    orderBy: { title: 'asc' }
                });

                // Group by type
                const byType: Record<string, any[]> = {};
                documents.forEach(doc => {
                    if (!byType[doc.type]) byType[doc.type] = [];
                    byType[doc.type].push({
                        ...doc,
                        isAcknowledged: doc.acks.length > 0 && doc.acks[0].version === doc.version
                    });
                });

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

    /**
     * POST /documents
     * Create new document (draft)
     */
    fastify.post('/', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;
                const data = createDocumentSchema.parse(request.body);

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

                await createAuditLog({
                    userId: user.id,
                    userName: user.name || user.email,
                    action: 'CREATE',
                    entity: 'Document',
                    entityId: document.id,
                    entityName: document.title,
                    after: document
                });

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

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

                const document = await prisma.document.findUnique({
                    where: { id },
                    include: {
                        createdBy: { select: { id: true, name: true } },
                        approvedBy: { select: { id: true, name: true } },
                        versions: { orderBy: { version: 'desc' } },
                        acks: { where: { userId: user.id } }
                    }
                });

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

                return {
                    ...document,
                    isAcknowledged: document.acks.length > 0 && document.acks[0].version === document.version
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch document' });
            }
        }
    });

    /**
     * PUT /documents/:id
     * Update document (creates new version)
     */
    fastify.put('/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;
                const data = updateDocumentSchema.parse(request.body);

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

                // Create version snapshot of current state
                await prisma.documentVersion.create({
                    data: {
                        documentId: id,
                        version: existing.version,
                        title: existing.title,
                        content: existing.content,
                        changedBy: user.id,
                        changeNotes: data.changeNotes
                    }
                });

                // Update document with new version
                const updated = await prisma.document.update({
                    where: { id },
                    data: {
                        ...data,
                        version: existing.version + 1,
                        status: 'DRAFT', // Reset to draft on edit
                        approvedById: null,
                        approvedAt: null
                    },
                    include: {
                        createdBy: { select: { id: true, name: true } }
                    }
                });

                await createAuditLog({
                    userId: user.id,
                    userName: user.name || user.email,
                    action: 'UPDATE',
                    entity: 'Document',
                    entityId: updated.id,
                    entityName: updated.title,
                    before: existing,
                    after: updated,
                    metadata: { changeNotes: data.changeNotes, newVersion: updated.version }
                });

                return updated;
            } 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 update document' });
            }
        }
    });

    /**
     * POST /documents/:id/submit
     * Submit document for review
     */
    fastify.post('/:id/submit', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

                const updated = await prisma.document.update({
                    where: { id },
                    data: { status: 'PENDING_REVIEW' }
                });

                await createAuditLog({
                    userId: user.id,
                    userName: user.name || user.email,
                    action: 'UPDATE',
                    entity: 'Document',
                    entityId: id,
                    entityName: updated.title,
                    metadata: { action: 'submit_for_review' }
                });

                return { success: true, status: updated.status };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to submit document' });
            }
        }
    });

    /**
     * POST /documents/:id/approve
     * Approve document
     */
    fastify.post('/:id/approve', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const user = request.user as any;

                const updated = await prisma.document.update({
                    where: { id },
                    data: {
                        status: 'APPROVED',
                        approvedById: user.id,
                        approvedAt: new Date()
                    }
                });

                await createAuditLog({
                    userId: user.id,
                    userName: user.name || user.email,
                    action: 'APPROVE',
                    entity: 'Document',
                    entityId: id,
                    entityName: updated.title
                });

                return { success: true, status: updated.status, approvedAt: updated.approvedAt };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to approve document' });
            }
        }
    });

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

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

                await prisma.documentAck.upsert({
                    where: {
                        documentId_userId: {
                            documentId: id,
                            userId: user.id
                        }
                    },
                    create: {
                        documentId: id,
                        userId: user.id,
                        version: document.version
                    },
                    update: {
                        version: document.version,
                        acknowledgedAt: new Date()
                    }
                });

                await createAuditLog({
                    userId: user.id,
                    userName: user.name || user.email,
                    action: 'ACCESS',
                    entity: 'Document',
                    entityId: id,
                    entityName: document.title,
                    metadata: { action: 'acknowledge', version: document.version }
                });

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

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

                const document = await prisma.document.findUnique({
                    where: { id },
                    include: {
                        acks: true
                    }
                });

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

                // Get target users
                const targetUsers = document.targetRoles.length > 0
                    ? await prisma.user.findMany({
                        where: { role: { in: document.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(
                    document.acks
                        .filter(a => a.version === document.version)
                        .map(a => a.userId)
                );

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

    /**
     * GET /documents/pending-acks
     * Get documents requiring acknowledgement for current user
     */
    fastify.get('/pending-acks', {
        handler: async (request, reply) => {
            try {
                const user = request.user as any;

                const documents = await prisma.document.findMany({
                    where: {
                        status: 'APPROVED',
                        requiresAck: true
                    },
                    include: {
                        acks: { where: { userId: user.id } }
                    }
                });

                const pending = documents.filter(doc =>
                    doc.acks.length === 0 || doc.acks[0].version < doc.version
                );

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