import { FastifyInstance, FastifyPluginOptions } from 'fastify';
import { PrismaClient, RoomType, SectionType } from '@prisma/client';

const prisma = new PrismaClient();

export async function layoutRoutes(fastify: FastifyInstance, options: FastifyPluginOptions) {

    // Authenticate all routes
    fastify.addHook('onRequest', async (request) => {
        try {
            await request.jwtVerify();
        } catch (err) {
            throw err;
        }
    });

    // ========================================
    // PROPERTY ROUTES
    // ========================================

    // Get all properties (with full hierarchy)
    fastify.get('/properties', {
        handler: async (request, reply) => {
            try {
                const properties = await prisma.facilityProperty.findMany({
                    include: {
                        buildings: {
                            include: {
                                floors: {
                                    include: {
                                        rooms: true
                                    },
                                    orderBy: { number: 'asc' }
                                }
                            }
                        }
                    }
                });
                return properties;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch properties' });
            }
        }
    });

    // Create property with default building and floor
    fastify.post('/properties', {
        handler: async (request, reply) => {
            try {
                const { name, address, licenseNum } = request.body as any;
                const property = await prisma.facilityProperty.create({
                    data: {
                        name,
                        address,
                        licenseNum,
                        buildings: {
                            create: {
                                name: 'Main Building',
                                code: 'MAIN',
                                type: 'CULTIVATION',
                                floors: {
                                    create: {
                                        name: 'Floor 1',
                                        number: 1,
                                        width: 200,
                                        height: 150
                                    }
                                }
                            }
                        }
                    },
                    include: {
                        buildings: {
                            include: { floors: true }
                        }
                    }
                });
                return reply.status(201).send(property);
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create property' });
            }
        }
    });

    // ========================================
    // BUILDING ROUTES
    // ========================================

    // Create building
    fastify.post('/buildings', {
        handler: async (request, reply) => {
            try {
                const { propertyId, name, code, type } = request.body as any;
                const building = await prisma.facilityBuilding.create({
                    data: {
                        propertyId,
                        name,
                        code,
                        type,
                        floors: {
                            create: {
                                name: 'Floor 1',
                                number: 1,
                                width: 200,
                                height: 150
                            }
                        }
                    },
                    include: { floors: true }
                });
                return reply.status(201).send(building);
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create building' });
            }
        }
    });

    // ========================================
    // FLOOR ROUTES
    // ========================================

    // Get floor with rooms
    fastify.get('/floors/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const floor = await prisma.facilityFloor.findUnique({
                    where: { id },
                    include: {
                        rooms: {
                            include: { sections: true }
                        },
                        building: {
                            include: { property: true }
                        }
                    }
                });

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

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

    // Create floor
    fastify.post('/floors', {
        handler: async (request, reply) => {
            try {
                const { buildingId, name, number, width, height, ceilingHeight, defaultTiers } = request.body as any;
                const floor = await prisma.facilityFloor.create({
                    data: {
                        buildingId,
                        name,
                        number,
                        width: width || 200,
                        height: height || 150,
                        ceilingHeight: ceilingHeight || null,
                        defaultTiers: defaultTiers || 1
                    },
                    include: { rooms: true }
                });
                return reply.status(201).send(floor);
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create floor' });
            }
        }
    });

    // ========================================
    // 3D VISUALIZATION ENDPOINT
    // ========================================

    // Get floor data optimized for 3D rendering
    fastify.get('/floors/:id/3d', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as { id: string };

                const floor = await prisma.facilityFloor.findUnique({
                    where: { id },
                    include: {
                        building: {
                            include: {
                                property: true
                            }
                        },
                        rooms: {
                            include: {
                                sections: {
                                    include: {
                                        positions: {
                                            include: {
                                                plant: {
                                                    include: {
                                                        batch: {
                                                            select: {
                                                                id: true,
                                                                name: true,
                                                                stage: true,
                                                                strain: true
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                });

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

                // Transform for 3D rendering
                const result = {
                    floor: {
                        id: floor.id,
                        name: floor.name,
                        number: floor.number,
                        width: floor.width,
                        height: floor.height,
                        building: floor.building.name,
                        property: floor.building.property.name
                    },
                    rooms: floor.rooms.map(room => ({
                        id: room.id,
                        name: room.name,
                        code: room.code,
                        type: room.type,
                        posX: room.posX,
                        posY: room.posY,
                        width: room.width,
                        height: room.height,
                        color: room.color,
                        sections: room.sections.map(section => ({
                            id: section.id,
                            name: section.name,
                            code: section.code,
                            type: section.type,
                            posX: section.posX,
                            posY: section.posY,
                            width: section.width,
                            height: section.height,
                            rows: section.rows,
                            columns: section.columns,
                            positions: section.positions.map(pos => ({
                                id: pos.id,
                                row: pos.row,
                                column: pos.column,
                                tier: pos.tier,
                                status: pos.status,
                                plant: pos.plant ? {
                                    id: pos.plant.id,
                                    tagNumber: pos.plant.tagNumber,
                                    status: pos.plant.status,
                                    batchId: pos.plant.batchId,
                                    batchName: pos.plant.batch?.name,
                                    strain: pos.plant.batch?.strain,
                                    stage: pos.plant.batch?.stage
                                } : null
                            }))
                        }))
                    })),
                    stats: {
                        totalRooms: floor.rooms.length,
                        totalSections: floor.rooms.reduce((sum, r) => sum + r.sections.length, 0),
                        totalPositions: floor.rooms.reduce((sum, r) =>
                            sum + r.sections.reduce((s, sec) => s + sec.positions.length, 0), 0
                        ),
                        occupiedPositions: floor.rooms.reduce((sum, r) =>
                            sum + r.sections.reduce((s, sec) =>
                                s + sec.positions.filter(p => p.status === 'OCCUPIED').length, 0
                            ), 0
                        )
                    }
                };

                return result;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch 3D floor data' });
            }
        }
    });

    // ========================================
    // ROOM ROUTES
    // ========================================

    // Create room
    fastify.post('/rooms', {
        handler: async (request, reply) => {
            try {
                const { floorId, name, code, type, posX, posY, width, height, color } = request.body as any;
                const room = await prisma.facilityRoom.create({
                    data: {
                        floorId,
                        name,
                        code,
                        type: type as RoomType,
                        posX,
                        posY,
                        width,
                        height,
                        color
                    }
                });
                return reply.status(201).send(room);
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create room' });
            }
        }
    });

    // Update room
    fastify.put('/rooms/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { name, code, type, posX, posY, width, height, color, rotation } = request.body as any;
                const room = await prisma.facilityRoom.update({
                    where: { id },
                    data: {
                        name,
                        code,
                        type: type as RoomType,
                        posX,
                        posY,
                        width,
                        height,
                        color,
                        rotation
                    }
                });
                return room;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to update room' });
            }
        }
    });

    // Delete room
    fastify.delete('/rooms/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                await prisma.facilityRoom.delete({ where: { id } });
                return reply.status(204).send();
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to delete room' });
            }
        }
    });

    // Get all sections for a room (with positions)
    fastify.get('/rooms/:id/sections', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const sections = await prisma.facilitySection.findMany({
                    where: { roomId: id },
                    include: {
                        positions: {
                            include: {
                                plant: true
                            },
                            orderBy: [{ row: 'asc' }, { column: 'asc' }]
                        }
                    }
                });
                return sections;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fetch room sections' });
            }
        }
    });

    // Occupy Position (Place Plant)
    fastify.post('/positions/:id/occupy', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { batchId } = request.body as any;

                const position = await prisma.facilityPosition.findUnique({
                    where: { id },
                    include: { section: { include: { room: true } } }
                });

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

                // Generate Address String (ROOM-SEC-R-C)
                const sec = position.section;
                const room = sec.room;
                const address = `${room.code}-${sec.code}-${position.row}-${position.column}`;

                await prisma.$transaction(async (tx) => {
                    // Check if already occupied
                    const existing = await tx.facilityPlant.findUnique({ where: { positionId: id } });
                    if (existing) throw new Error('Position already occupied');

                    await tx.facilityPlant.create({
                        data: {
                            tagNumber: `P-${Date.now()}-${Math.floor(Math.random() * 1000)}`,
                            batchId,
                            positionId: id,
                            address,
                            status: 'ACTIVE'
                        }
                    });

                    await tx.facilityPosition.update({
                        where: { id },
                        data: { status: 'PLANTED' }
                    });
                });

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

    // Move Plant to a new position
    fastify.post('/plants/:id/move', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { targetPositionId, reason } = request.body as any;
                const userId = (request.user as any)?.id || 'system';

                // Get plant with current position
                const plant = await prisma.facilityPlant.findUnique({
                    where: { id },
                    include: {
                        position: {
                            include: { section: { include: { room: true } } }
                        }
                    }
                });

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

                // Get target position
                const targetPosition = await prisma.facilityPosition.findUnique({
                    where: { id: targetPositionId },
                    include: { section: { include: { room: true } }, plant: true }
                });

                if (!targetPosition) return reply.status(404).send({ error: 'Target position not found' });
                if (targetPosition.plant) return reply.status(400).send({ error: 'Target position is occupied' });

                const oldAddress = plant.address;
                const newRoom = targetPosition.section.room;
                const newSection = targetPosition.section;
                const newAddress = `${newRoom.code}-${newSection.code}-${targetPosition.row}-${targetPosition.column}`;

                await prisma.$transaction(async (tx) => {
                    // Clear old position
                    await tx.facilityPosition.update({
                        where: { id: plant.positionId },
                        data: { status: 'EMPTY' }
                    });

                    // Update plant
                    await tx.facilityPlant.update({
                        where: { id },
                        data: {
                            positionId: targetPositionId,
                            address: newAddress
                        }
                    });

                    // Update new position
                    await tx.facilityPosition.update({
                        where: { id: targetPositionId },
                        data: { status: 'PLANTED' }
                    });

                    // Record history
                    await tx.plantLocationHistory.create({
                        data: {
                            plantId: id,
                            fromAddress: oldAddress,
                            toAddress: newAddress,
                            movedById: userId,
                            reason: reason || 'REORGANIZE'
                        }
                    });
                });

                return {
                    success: true,
                    newAddress,
                    message: `Moved plant from ${oldAddress} to ${newAddress}`
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to move plant' });
            }
        }
    });

    // Bulk Fill Section (Place plants in all empty positions)
    fastify.post('/sections/:id/fill', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const { batchId, maxCount } = request.body as any;

                // Get section with room info for address generation
                const section = await prisma.facilitySection.findUnique({
                    where: { id },
                    include: {
                        room: true,
                        positions: {
                            where: { status: 'EMPTY' },
                            orderBy: [{ row: 'asc' }, { column: 'asc' }]
                        }
                    }
                });

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

                const emptyPositions = section.positions;
                const positionsToFill = maxCount
                    ? emptyPositions.slice(0, maxCount)
                    : emptyPositions;

                if (positionsToFill.length === 0) {
                    return reply.status(400).send({ error: 'No empty positions available' });
                }

                const room = section.room;
                let plantsCreated = 0;

                await prisma.$transaction(async (tx) => {
                    for (const pos of positionsToFill) {
                        const address = `${room.code}-${section.code}-${pos.row}-${pos.column}`;

                        await tx.facilityPlant.create({
                            data: {
                                tagNumber: `P-${Date.now()}-${pos.row}${pos.column}-${Math.floor(Math.random() * 100)}`,
                                batchId,
                                positionId: pos.id,
                                address,
                                status: 'ACTIVE'
                            }
                        });

                        await tx.facilityPosition.update({
                            where: { id: pos.id },
                            data: { status: 'PLANTED' }
                        });

                        plantsCreated++;
                    }
                });

                return {
                    success: true,
                    plantsCreated,
                    message: `Placed ${plantsCreated} plants in ${section.name}`
                };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to fill section' });
            }
        }
    });

    // ========================================
    // SECTION ROUTES
    // ========================================

    // Create section with positions
    fastify.post('/sections', {
        handler: async (request, reply) => {
            try {
                const { roomId, name, code, type, posX, posY, width, height, rows, columns, spacing, tiers } = request.body as any;
                const numTiers = tiers || 1;

                // Generate positions for all rows, columns, and tiers
                const positionsToCreate = [];
                for (let r = 1; r <= rows; r++) {
                    for (let c = 1; c <= columns; c++) {
                        for (let t = 1; t <= numTiers; t++) {
                            positionsToCreate.push({
                                row: r,
                                column: c,
                                tier: t,
                                slot: 1,
                                status: 'EMPTY'
                            });
                        }
                    }
                }

                const section = await prisma.facilitySection.create({
                    data: {
                        roomId,
                        name,
                        code,
                        type: type as SectionType,
                        posX,
                        posY,
                        width,
                        height,
                        rows,
                        columns,
                        tiers: numTiers,
                        spacing: spacing || 12,
                        positions: {
                            create: positionsToCreate
                        }
                    },
                    include: { positions: true }
                });

                return reply.status(201).send(section);
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to create section' });
            }
        }
    });

    // Get section with positions
    fastify.get('/sections/:id', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const section = await prisma.facilitySection.findUnique({
                    where: { id },
                    include: {
                        positions: {
                            include: { plant: true },
                            orderBy: [{ row: 'asc' }, { column: 'asc' }]
                        },
                        room: true
                    }
                });

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

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

    // ========================================
    // BULK SAVE LAYOUT
    // ========================================

    // Save entire floor layout
    fastify.post('/floors/:id/layout', {
        handler: async (request, reply) => {
            try {
                const { id: floorId } = request.params as any;
                const { rooms } = request.body as any;

                await prisma.$transaction(async (tx) => {
                    // Get existing room IDs
                    const existingRooms = await tx.facilityRoom.findMany({
                        where: { floorId },
                        select: { id: true }
                    });
                    const existingIds = new Set(existingRooms.map(r => r.id));
                    const incomingIds = new Set((rooms as any[]).map(r => r.id));

                    // Process each room
                    for (const room of rooms) {
                        const roomData = {
                            name: room.name,
                            code: room.code,
                            type: room.type,
                            posX: room.posX,
                            posY: room.posY,
                            width: room.width,
                            height: room.height,
                            color: room.color,
                            rotation: room.rotation || 0
                        };

                        if (existingIds.has(room.id)) {
                            // Update existing room
                            await tx.facilityRoom.update({
                                where: { id: room.id },
                                data: roomData
                            });
                        } else {
                            // Create new room
                            await tx.facilityRoom.create({
                                data: {
                                    id: room.id,
                                    floorId,
                                    ...roomData
                                }
                            });
                        }

                        // Handle Sections
                        const sections = room.sections || [];
                        const existingSections = await tx.facilitySection.findMany({
                            where: { roomId: room.id },
                            select: { id: true }
                        });
                        const existingSecIds = new Set(existingSections.map(s => s.id));
                        const incomingSecIds = new Set(sections.map((s: any) => s.id));

                        for (const sec of sections) {
                            const secData = {
                                roomId: room.id,
                                name: sec.name,
                                code: sec.code,
                                type: sec.type,
                                posX: sec.posX,
                                posY: sec.posY,
                                width: sec.width,
                                height: sec.height,
                                rows: sec.rows,
                                columns: sec.columns,
                                tiers: sec.tiers || 1,
                                spacing: sec.spacing || 12
                            };

                            if (existingSecIds.has(sec.id)) {
                                // Update existing section
                                await tx.facilitySection.update({
                                    where: { id: sec.id },
                                    data: secData
                                });
                            } else {
                                // Create new section with positions
                                const numTiers = sec.tiers || 1;
                                const positionsToCreate = [];
                                for (let r = 1; r <= sec.rows; r++) {
                                    for (let c = 1; c <= sec.columns; c++) {
                                        for (let t = 1; t <= numTiers; t++) {
                                            positionsToCreate.push({
                                                row: r,
                                                column: c,
                                                tier: t,
                                                slot: 1,
                                                status: 'EMPTY'
                                            });
                                        }
                                    }
                                }

                                await tx.facilitySection.create({
                                    data: {
                                        id: sec.id,
                                        ...secData,
                                        positions: {
                                            create: positionsToCreate
                                        }
                                    }
                                });
                            }
                        }

                        // Delete removed sections
                        const secToDelete = [...existingSecIds].filter(id => !incomingSecIds.has(id));
                        if (secToDelete.length > 0) {
                            await tx.facilitySection.deleteMany({
                                where: { id: { in: secToDelete } }
                            });
                        }
                    }

                    // Delete rooms that were removed
                    const toDelete = [...existingIds].filter(id => !incomingIds.has(id));
                    if (toDelete.length > 0) {
                        await tx.facilityRoom.deleteMany({
                            where: { id: { in: toDelete } }
                        });
                    }
                });

                // Return updated floor
                const floor = await prisma.facilityFloor.findUnique({
                    where: { id: floorId },
                    include: { rooms: { include: { sections: true } } }
                });

                return floor;
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to save layout' });
            }
        }
    });

    // ========================================
    // POSITION ROUTES
    // ========================================

    // Get hierarchical address for a position
    fastify.get('/positions/:id/address', {
        handler: async (request, reply) => {
            try {
                const { id } = request.params as any;
                const position = await prisma.facilityPosition.findUnique({
                    where: { id },
                    include: {
                        section: {
                            include: {
                                room: {
                                    include: {
                                        floor: {
                                            include: {
                                                building: {
                                                    include: {
                                                        property: true
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                });

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

                // Construct hierarchical address
                // Format: PROP.BLDG.FLR.RM.SEC.R{Row}.C{Col}.S{Slot}
                const p = position.section.room.floor.building.property;
                const b = position.section.room.floor.building;
                const f = position.section.room.floor;
                const r = position.section.room;
                const s = position.section;

                // Helper to sanitize codes (remove spaces, uppercase)
                const code = (str: string) => str.toUpperCase().replace(/[^A-Z0-9]/g, '');

                const address = [
                    code(p.name).substring(0, 4),
                    code(b.code),
                    `F${f.number}`,
                    code(r.code),
                    code(s.code),
                    `R${position.row}`,
                    `C${position.column}`
                ].join('.');

                return { address, details: position };
            } catch (error) {
                fastify.log.error(error);
                return reply.status(500).send({ error: 'Failed to generate address' });
            }
        }
    });
}
