import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/prisma';
import { requireAuthenticatedUser, canModifyJourney, unauthorizedResponse } from '@/lib/api-auth';
import { extractExifMetadata } from '@/lib/exif';
import { writeFile, mkdir } from 'fs/promises';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
import sharp from 'sharp';

export async function POST(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
    try {
        const session = await requireAuthenticatedUser();
        if (!session) return unauthorizedResponse();

        const { id: journeyId } = await params;

        const journey = await prisma.journey.findUnique({ where: { id: journeyId } });
        if (!journey) return NextResponse.json({ error: 'Journey not found' }, { status: 404 });
        if (!canModifyJourney(session as { user: { id: string; role?: string } }, journey.userId))
            return NextResponse.json({ error: 'Forbidden' }, { status: 403 });

        const contentType = req.headers.get('content-type') || '';

        // Image upload (multipart)
        if (contentType.includes('multipart/form-data')) {
            const formData = await req.formData();
            const file = formData.get('file') as File;
            const orderStr = formData.get('order') as string;

            if (!file) return NextResponse.json({ error: 'No file' }, { status: 400 });

            const ALLOWED = ['image/jpeg', 'image/png', 'image/webp', 'image/tiff', 'image/heic', 'image/heif'];
            if (!ALLOWED.includes(file.type))
                return NextResponse.json({ error: `Invalid file type: ${file.type}` }, { status: 400 });
            if (file.size > 50 * 1024 * 1024)
                return NextResponse.json({ error: 'File too large (max 50MB)' }, { status: 400 });

            const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'journeys', journeyId);
            await mkdir(uploadDir, { recursive: true });

            const buffer = Buffer.from(await file.arrayBuffer());
            const metadata = await extractExifMetadata(buffer);
            const safeFilename = path.basename(file.name);
            const filename = `${uuidv4()}-${safeFilename}`;
            const filepath = path.join(uploadDir, filename);

            try {
                await sharp(buffer)
                    .rotate()
                    .resize(2500, 2500, { fit: 'inside', withoutEnlargement: true })
                    .jpeg({ quality: 85, mozjpeg: true })
                    .withMetadata()
                    .toFile(filepath);
            } catch {
                await writeFile(filepath, buffer);
            }

            const order = orderStr ? parseInt(orderStr, 10) : await getNextOrder(journeyId);

            // Shift existing blocks down if inserting
            if (orderStr) {
                await prisma.journeyBlock.updateMany({
                    where: { journeyId, order: { gte: order } },
                    data: { order: { increment: 1 } },
                });
            }

            const block = await prisma.journeyBlock.create({
                data: {
                    journeyId,
                    type: 'image',
                    imageUrl: `/uploads/journeys/${journeyId}/${filename}`,
                    filename: safeFilename,
                    text: metadata.caption,
                    latitude: metadata.latitude,
                    longitude: metadata.longitude,
                    order,
                },
            });

            // Auto-set cover
            if (!journey.coverPhotoUrl) {
                await prisma.journey.update({
                    where: { id: journeyId },
                    data: { coverPhotoUrl: block.imageUrl },
                });
            }

            return NextResponse.json(block, { status: 201 });
        }

        // JSON body (text, location, or archive image)
        const body = await req.json();
        const { type, text, photoId, latitude, longitude, locationName, order: bodyOrder } = body;

        if (!type || !['text', 'image', 'gallery', 'location'].includes(type))
            return NextResponse.json({ error: 'Invalid block type' }, { status: 400 });

        const order = bodyOrder !== undefined ? bodyOrder : await getNextOrder(journeyId);

        // Shift down if inserting
        if (bodyOrder !== undefined) {
            await prisma.journeyBlock.updateMany({
                where: { journeyId, order: { gte: order } },
                data: { order: { increment: 1 } },
            });
        }

        const data: Record<string, unknown> = {
            journeyId,
            type,
            order,
        };

        if (type === 'text') {
            data.text = text || '';
        } else if (type === 'image' && photoId) {
            // Archive photo reference
            const photo = await prisma.photo.findUnique({ where: { id: photoId } });
            if (!photo) return NextResponse.json({ error: 'Photo not found' }, { status: 404 });
            data.photoId = photo.id;
            data.imageUrl = photo.url;
            data.filename = photo.filename;
            data.text = photo.caption;
            data.latitude = photo.latitude;
            data.longitude = photo.longitude;
        } else if (type === 'gallery' && body.photoIds) {
            // Multiple archive photos → gallery block
            const photos = await prisma.photo.findMany({
                where: { id: { in: body.photoIds } },
            });
            const galleryImages = photos.map(p => ({
                url: p.url,
                caption: p.caption,
                photoId: p.id,
            }));
            data.images = JSON.stringify(galleryImages);
            // Use first photo as cover image for the block
            if (photos[0]) {
                data.imageUrl = photos[0].url;
            }
        } else if (type === 'location') {
            data.latitude = latitude ?? null;
            data.longitude = longitude ?? null;
            data.locationName = locationName || null;
            data.text = text || null;
        }

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

        return NextResponse.json(block, { status: 201 });
    } catch (error) {
        console.error('Block POST error:', error);
        return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
    }
}

// Reorder blocks
export async function PUT(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
    try {
        const session = await requireAuthenticatedUser();
        if (!session) return unauthorizedResponse();

        const { id: journeyId } = await params;
        const journey = await prisma.journey.findUnique({ where: { id: journeyId } });
        if (!journey) return NextResponse.json({ error: 'Journey not found' }, { status: 404 });
        if (!canModifyJourney(session as { user: { id: string; role?: string } }, journey.userId))
            return NextResponse.json({ error: 'Forbidden' }, { status: 403 });

        const { orderedIds } = await req.json();
        if (!Array.isArray(orderedIds))
            return NextResponse.json({ error: 'orderedIds required' }, { status: 400 });

        await Promise.all(
            orderedIds.map((id: string, i: number) =>
                prisma.journeyBlock.update({ where: { id }, data: { order: i } })
            )
        );

        return NextResponse.json({ success: true });
    } catch (error) {
        console.error('Blocks reorder error:', error);
        return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
    }
}

async function getNextOrder(journeyId: string): Promise<number> {
    const max = await prisma.journeyBlock.aggregate({
        where: { journeyId },
        _max: { order: true },
    });
    return (max._max.order ?? -1) + 1;
}
