import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { pipeline } from 'stream/promises';
import { createWriteStream } from 'fs';
import { unlink, mkdir, stat } from 'fs/promises';
import { join, extname } from 'path';
import { randomUUID } from 'crypto';
import { Prisma } from '@prisma/client';
import { prisma } from '../../../config/database';
import { requireAdminRole } from '../middleware/auth';
import { logger } from '../../../utils/logger';

const DOCUMENT_INBOX_DIR = '/media/local/documents/inbox';

const ALLOWED_DOCUMENT_EXTENSIONS = [
  '.pdf',
  '.doc',
  '.docx',
  '.xlsx',
  '.csv',
  '.txt',
  '.md',
  '.zip',
];

const ALLOWED_DOCUMENT_MIMETYPES = new Set([
  'application/pdf',
  'application/msword',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  'application/vnd.ms-excel',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'text/csv',
  'application/csv',
  'text/plain',
  'text/markdown',
  'application/zip',
  'application/x-zip-compressed',
  'application/octet-stream',
]);

function parseTags(raw: string | undefined): string[] | null {
  if (!raw) return null;
  try {
    const parsed = JSON.parse(raw);
    if (Array.isArray(parsed) && parsed.every((t) => typeof t === 'string')) {
      return parsed;
    }
  } catch {
    // Fall through: allow plain comma-separated fallback
  }
  const split = raw.split(',').map((t) => t.trim()).filter(Boolean);
  return split.length > 0 ? split : null;
}

export async function documentUploadRoutes(fastify: FastifyInstance) {
  fastify.post(
    '/upload',
    { preHandler: requireAdminRole },
    async (request: FastifyRequest, reply: FastifyReply) => {
      let tempFilePath: string | null = null;

      try {
        const data = await request.file();
        if (!data) {
          return reply.code(400).send({ message: 'No file uploaded' });
        }

        const ext = extname(data.filename).toLowerCase();
        if (!ALLOWED_DOCUMENT_EXTENSIONS.includes(ext)) {
          return reply.code(400).send({
            message: `Invalid file type. Allowed: ${ALLOWED_DOCUMENT_EXTENSIONS.join(', ')}`,
          });
        }

        const mimeType = data.mimetype || 'application/octet-stream';
        if (!ALLOWED_DOCUMENT_MIMETYPES.has(mimeType)) {
          return reply.code(400).send({
            message: `Invalid mime type: ${mimeType}`,
          });
        }

        const filename = `${randomUUID()}${ext}`;
        await mkdir(DOCUMENT_INBOX_DIR, { recursive: true });

        const filePath = join(DOCUMENT_INBOX_DIR, filename);
        tempFilePath = filePath;

        logger.info(`Uploading document to ${filePath}`);
        await pipeline(data.file, createWriteStream(filePath));

        const fileStat = await stat(filePath);

        const fields = data.fields as Record<string, { value: string }>;
        const title = fields.title?.value?.trim() || null;
        const description = fields.description?.value?.trim() || null;
        const category = fields.category?.value?.trim() || null;
        const tags = parseTags(fields.tags?.value);

        const document = await prisma.document.create({
          data: {
            path: filePath,
            filename,
            originalFilename: data.filename,
            title: title || data.filename,
            description,
            mimeType,
            fileSize: BigInt(fileStat.size),
            category,
            tags: tags ? (tags as unknown as Prisma.InputJsonValue) : Prisma.JsonNull,
            uploaderId: request.user?.id || null,
          },
        });

        logger.info(`Document uploaded: ${document.id}`);

        return reply.code(201).send({
          message: 'Document uploaded successfully',
          document: {
            ...document,
            fileSize: document.fileSize?.toString() ?? null,
          },
        });
      } catch (error) {
        if (tempFilePath) {
          try { await unlink(tempFilePath); } catch { /* ignore */ }
        }
        logger.error('Document upload failed:', error);
        return reply.code(500).send({
          message: error instanceof Error ? error.message : 'Upload failed',
        });
      }
    }
  );
}
