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

const buildDestinationPath = (companyId, 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, companyId);
  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 ensureCompanyExists = async (companyId) => {
  const [company] = await db
    .select()
    .from(companies)
    .where(eq(companies.id, companyId))
    .limit(1);

  if (!company) {
    throw new NotFoundError('Firma nenájdená');
  }

  return company;
};

/**
 * Get all documents for a company
 */
export const getDocumentsByCompanyId = async (companyId) => {
  await ensureCompanyExists(companyId);

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

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

  await ensureCompanyExists(companyId);

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

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

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

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

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

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

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

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

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