import { FastifyPluginAsync } from 'fastify';
import { z } from 'zod';
import { db } from '../config/database.js';
import { galleryImages } from '../db/schema.js';
import { eq } from 'drizzle-orm';
import fs from 'fs';
import path from 'path';

// Fastify JSON schema for gallery image body
const galleryBodyJsonSchema = {
  type: 'object',
  required: ['imageUrl', 'altText', 'displayOrder'],
  properties: {
    imageUrl: { type: 'string', minLength: 1 },
    altText: { type: 'string', minLength: 1, maxLength: 200 },
    displayOrder: { type: 'integer', minimum: 0 },
    isPublished: { type: 'boolean' },
  },
} as const;

const galleryRoute: FastifyPluginAsync = async (fastify) => {

  // PUBLIC: List published gallery images (no auth required)
  fastify.get('/gallery/public', async () => {
    const images = await db.select().from(galleryImages)
      .where(eq(galleryImages.isPublished, true))
      .orderBy(galleryImages.displayOrder);
    return { images };
  });

  // List all gallery images - admin only
  fastify.get('/gallery', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const images = await db.select().from(galleryImages).orderBy(galleryImages.displayOrder);
    return { images };
  });

  // Get single gallery image
  fastify.get('/gallery/:id', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const { id } = request.params as { id: string };
    const image = await db.select().from(galleryImages).where(eq(galleryImages.id, id)).limit(1);

    if (image.length === 0) {
      return reply.code(404).send({ error: 'Image not found' });
    }

    return { image: image[0] };
  });

  // Create gallery image
  fastify.post('/gallery', {
    schema: {
      body: galleryBodyJsonSchema,
    },
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const data = request.body as any;

    const [newImage] = await db.insert(galleryImages).values(data).returning();

    return reply.code(201).send({ image: newImage });
  });

  // Upload image file (multipart)
  fastify.post('/gallery/upload', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    try {
      // Expect a single file field named "file"
      const file = await (request as any).file();
      if (!file) {
        return reply.code(400).send({ error: 'No file uploaded' });
      }

      const altText = (file.fields?.altText?.value as string | undefined) || '';
      const displayOrderRaw = (file.fields?.displayOrder?.value as string | undefined) || '0';
      const displayOrder = Number.parseInt(displayOrderRaw) || 0;

      const mime = file.mimetype as string | undefined;
      if (!mime || !mime.startsWith('image/')) {
        return reply.code(400).send({ error: 'Only image uploads are allowed' });
      }

      // Prepare directories - use persistent volume for Fly.io
      const dataDir = process.env.GIT_WORKSPACE_DIR || path.join(process.cwd(), 'data');
      const uploadDir = path.join(dataDir, 'public', 'images', 'gallery');
      if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });

      // Read uploaded stream into buffer
      const chunks: Buffer[] = [];
      for await (const chunk of file.file) {
        chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
      }
      const inputBuffer = Buffer.concat(chunks);

      // Generate filename
      const stamp = Date.now().toString(36);
      const rand = Math.random().toString(36).slice(2, 8);
      const baseName = `${stamp}-${rand}`;

      // Try to convert to webp and limit size; fallback to original
      let outBuffer: Buffer | null = null;
      let outExt = '.webp';
      try {
        // Lazy load sharp only when needed
        const sharp = (await import('sharp')).default;
        outBuffer = await sharp(inputBuffer)
          .rotate()
          .resize({ width: 1600, withoutEnlargement: true })
          .webp({ quality: 82 })
          .toBuffer();
      } catch (err) {
        fastify.log.warn({ err }, 'Sharp processing failed, using original image');
        outBuffer = inputBuffer;
        // naive extension from mimetype
        const extFromMime = mime.split('/')[1] || 'bin';
        outExt = '.' + extFromMime.replace(/[^a-z0-9]/gi, '').toLowerCase();
      }

      const filename = baseName + outExt;
      const destPath = path.join(uploadDir, filename);
      fs.writeFileSync(destPath, outBuffer);

      // Public URL (served via /static)
      const publicUrl = `/images/gallery/${filename}`;

      // Store in DB (optional but useful)
      const [row] = await db.insert(galleryImages).values({
        imageUrl: publicUrl,
        altText: altText || filename,
        displayOrder,
        isPublished: true,
      }).returning();

      return reply.code(201).send({ image: row });

    } catch (err) {
      fastify.log.error({ err }, 'Upload failed');
      return reply.code(500).send({ error: 'Failed to upload image' });
    }
  });

  // Update gallery image
  fastify.put('/gallery/:id', {
    schema: {
      body: galleryBodyJsonSchema,
    },
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const { id } = request.params as { id: string };
    const data = request.body as any;

    const [updated] = await db
      .update(galleryImages)
      .set(data)
      .where(eq(galleryImages.id, id))
      .returning();

    if (!updated) {
      return reply.code(404).send({ error: 'Image not found' });
    }

    return { image: updated };
  });

  // Delete gallery image
  fastify.delete('/gallery/:id', {
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const { id } = request.params as { id: string };

    const [deleted] = await db
      .delete(galleryImages)
      .where(eq(galleryImages.id, id))
      .returning();

    if (!deleted) {
      return reply.code(404).send({ error: 'Image not found' });
    }

    return { message: 'Image deleted successfully' };
  });

  // Reorder gallery images
  fastify.put('/gallery/reorder', {
    schema: {
      body: {
        type: 'object',
        required: ['orders'],
        properties: {
          orders: {
            type: 'array',
            items: {
              type: 'object',
              required: ['id', 'displayOrder'],
              properties: {
                id: { type: 'string' },
                displayOrder: { type: 'integer', minimum: 0 },
              },
            },
          },
        },
      },
    },
    preHandler: [fastify.authenticate],
  }, async (request, reply) => {
    const { orders } = request.body as { orders: Array<{ id: string; displayOrder: number }> };

    // Update all in synchronous transaction (better-sqlite3 requirement)
    db.transaction((tx: any) => {
      for (const { id, displayOrder } of orders) {
        tx.update(galleryImages).set({ displayOrder }).where(eq(galleryImages.id, id)).run?.();
      }
    });

    return { message: 'Gallery images reordered successfully' };
  });
};

export default galleryRoute;
