import fs from 'fs/promises';
import path from 'path';
import { db } from '../config/database.js';
import { serviceDocuments, serviceFolders, users } from '../db/schema.js';
import { and, desc, eq } from 'drizzle-orm';
import { BadRequestError, NotFoundError } from '../utils/errors.js';
import { logger } from '../utils/logger.js';

const BASE_UPLOAD_DIR = path.join(process.cwd(), 'uploads', 'service-documents');

const buildDestinationPath = (folderId, originalName) => {
  const ext = path.extname(originalName);
  const name = path.basename(originalName, ext);
  const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
  const filename = `${name}-${uniqueSuffix}${ext}`;
  const folder = path.join(BASE_UPLOAD_DIR, folderId);
  const filePath = path.join(folder, filename);

  return { folder, filename, filePath };
};

const safeUnlink = async (filePath) => {
  if (!filePath) return;
  try {
    await fs.unlink(filePath);
  } catch (error) {
    logger.error('Failed to delete file', error);
  }
};

const ensureFolderExists = async (folderId) => {
  const [folder] = await db
    .select()
    .from(serviceFolders)
    .where(eq(serviceFolders.id, folderId))
    .limit(1);

  if (!folder) {
    throw new NotFoundError('Priečinok nenájdený');
  }

  return folder;
};

/**
 * Get all documents in a folder
 */
export const getDocumentsByFolderId = async (folderId) => {
  await ensureFolderExists(folderId);

  return db
    .select({
      id: serviceDocuments.id,
      folderId: serviceDocuments.folderId,
      fileName: serviceDocuments.fileName,
      originalName: serviceDocuments.originalName,
      fileType: serviceDocuments.fileType,
      fileSize: serviceDocuments.fileSize,
      description: serviceDocuments.description,
      uploadedAt: serviceDocuments.uploadedAt,
      uploadedBy: serviceDocuments.uploadedBy,
      uploaderUsername: users.username,
      uploaderFirstName: users.firstName,
      uploaderLastName: users.lastName,
    })
    .from(serviceDocuments)
    .leftJoin(users, eq(serviceDocuments.uploadedBy, users.id))
    .where(eq(serviceDocuments.folderId, folderId))
    .orderBy(desc(serviceDocuments.uploadedAt));
};

/**
 * Upload a document to a folder
 */
export const uploadDocument = async ({ folderId, userId, file, description }) => {
  if (!file) {
    throw new BadRequestError('Súbor nebol nahraný');
  }

  await ensureFolderExists(folderId);

  const { folder, filename, filePath } = buildDestinationPath(folderId, file.originalname);

  await fs.mkdir(folder, { recursive: true });

  try {
    await fs.writeFile(filePath, file.buffer);

    const [newDoc] = await db
      .insert(serviceDocuments)
      .values({
        folderId,
        fileName: filename,
        originalName: file.originalname,
        filePath,
        fileType: file.mimetype,
        fileSize: file.size,
        description: description || null,
        uploadedBy: userId,
      })
      .returning();

    return newDoc;
  } catch (error) {
    await safeUnlink(filePath);
    throw error;
  }
};

/**
 * Get document by ID for download
 */
export const getDocumentForDownload = async (folderId, documentId) => {
  await ensureFolderExists(folderId);

  const [doc] = await db
    .select()
    .from(serviceDocuments)
    .where(and(
      eq(serviceDocuments.id, documentId),
      eq(serviceDocuments.folderId, folderId)
    ))
    .limit(1);

  if (!doc) {
    throw new NotFoundError('Dokument nenájdený');
  }

  try {
    await fs.access(doc.filePath);
  } catch {
    throw new NotFoundError('Súbor nebol nájdený na serveri');
  }

  return {
    document: doc,
    filePath: doc.filePath,
    fileName: doc.originalName,
    fileType: doc.fileType,
  };
};

/**
 * Delete a document
 */
export const deleteDocument = async (folderId, documentId) => {
  await ensureFolderExists(folderId);

  const [doc] = await db
    .select()
    .from(serviceDocuments)
    .where(and(
      eq(serviceDocuments.id, documentId),
      eq(serviceDocuments.folderId, folderId)
    ))
    .limit(1);

  if (!doc) {
    throw new NotFoundError('Dokument nenájdený');
  }

  await safeUnlink(doc.filePath);
  await db.delete(serviceDocuments).where(eq(serviceDocuments.id, documentId));

  return { success: true, message: 'Dokument bol odstránený' };
};
