import fs from 'fs/promises';
import path from 'path';
import { db } from '../config/database.js';
import { projectDocuments, projects, 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', 'project-documents');

const buildDestinationPath = (projectId, 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, projectId);
  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 ensureProjectExists = async (projectId) => {
  const [project] = await db
    .select()
    .from(projects)
    .where(eq(projects.id, projectId))
    .limit(1);

  if (!project) {
    throw new NotFoundError('Projekt nenájdený');
  }

  return project;
};

/**
 * Get all documents for a project
 */
export const getDocumentsByProjectId = async (projectId) => {
  await ensureProjectExists(projectId);

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

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

  await ensureProjectExists(projectId);

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

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

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

    const [newDoc] = await db
      .insert(projectDocuments)
      .values({
        projectId,
        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 (projectId, documentId) => {
  await ensureProjectExists(projectId);

  const [doc] = await db
    .select()
    .from(projectDocuments)
    .where(and(
      eq(projectDocuments.id, documentId),
      eq(projectDocuments.projectId, projectId)
    ))
    .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 (projectId, documentId) => {
  await ensureProjectExists(projectId);

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

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

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

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