import fs from 'fs/promises';
import path from 'path';
import { db } from '../config/database.js';
import { timesheets, users } from '../db/schema.js';
import { and, desc, eq } from 'drizzle-orm';
import { BadRequestError, ForbiddenError, NotFoundError } from '../utils/errors.js';
import { logger } from '../utils/logger.js';
import { logTimesheetUploaded, logTimesheetDeleted } from './audit.service.js';

const ALLOWED_MIME_TYPES = [
  'application/pdf',
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  'application/vnd.ms-excel',
];

const BASE_UPLOAD_DIR = path.join(process.cwd(), 'uploads', 'timesheets');

const sanitizeTimesheet = (record) => ({
  id: record.id,
  fileName: record.fileName,
  fileType: record.fileType,
  fileSize: record.fileSize,
  year: record.year,
  month: record.month,
  isGenerated: record.isGenerated,
  uploadedAt: record.uploadedAt,
});

const detectFileType = (mimeType) => {
  if (!ALLOWED_MIME_TYPES.includes(mimeType)) {
    throw new BadRequestError('Neplatný typ súboru. Povolené sú iba PDF a Excel súbory.');
  }
  return mimeType.includes('sheet') || mimeType.includes('excel') ? 'xlsx' : 'pdf';
};

const buildDestinationPath = (userId, year, month, 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, userId, year.toString(), month.toString());
  const filePath = path.join(folder, filename);

  return { folder, filename, filePath };
};

const ensureTimesheetExists = async (timesheetId) => {
  const [timesheet] = await db
    .select()
    .from(timesheets)
    .where(eq(timesheets.id, timesheetId))
    .limit(1);

  if (!timesheet) {
    throw new NotFoundError('Timesheet nenájdený');
  }

  return timesheet;
};

export const getTimesheetById = async (timesheetId) => {
  return ensureTimesheetExists(timesheetId);
};

const assertAccess = (timesheet, { userId, role }) => {
  const hasFullAccess = role === 'admin' || role === 'team_leader';
  if (!hasFullAccess && timesheet.userId !== userId) {
    throw new ForbiddenError('Nemáte oprávnenie k tomuto timesheetu');
  }
};

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

const generateTimesheetFileName = (firstName, lastName, username, year, month, fileExt) => {
  let namePrefix;
  if (firstName && lastName) {
    namePrefix = `${firstName}-${lastName}`.toLowerCase().replace(/\s+/g, '-');
  } else if (firstName) {
    namePrefix = firstName.toLowerCase().replace(/\s+/g, '-');
  } else if (username) {
    namePrefix = username.toLowerCase().replace(/\s+/g, '-');
  } else {
    namePrefix = 'timesheet';
  }
  const monthStr = String(month).padStart(2, '0');
  return `${namePrefix}-vykazprace-${year}-${monthStr}${fileExt}`;
};

export const uploadTimesheet = async ({ userId, year, month, file, auditContext = null }) => {
  if (!file) {
    throw new BadRequestError('Súbor nebol nahraný');
  }

  // Fetch user info for filename generation
  const [user] = await db
    .select({ firstName: users.firstName, lastName: users.lastName, username: users.username })
    .from(users)
    .where(eq(users.id, userId))
    .limit(1);

  const parsedYear = parseInt(year);
  const parsedMonth = parseInt(month);
  const fileType = detectFileType(file.mimetype);
  const fileExt = path.extname(file.originalname);
  const { folder, filename, filePath } = buildDestinationPath(userId, parsedYear, parsedMonth, file.originalname);

  // Generate user-friendly filename
  const displayFileName = generateTimesheetFileName(
    user?.firstName,
    user?.lastName,
    user?.username,
    parsedYear,
    parsedMonth,
    fileExt
  );

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

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

    const [newTimesheet] = await db
      .insert(timesheets)
      .values({
        userId,
        fileName: displayFileName,
        filePath,
        fileType,
        fileSize: file.size,
        year: parsedYear,
        month: parsedMonth,
        isGenerated: false,
      })
      .returning();

    const sanitized = sanitizeTimesheet(newTimesheet);

    if (auditContext) {
      await logTimesheetUploaded(auditContext.userId, newTimesheet.id, parsedYear, parsedMonth, auditContext.ipAddress, auditContext.userAgent);
    }

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

export const getTimesheetsForUser = async (userId, { year, month } = {}) => {
  const conditions = [eq(timesheets.userId, userId)];

  if (year) {
    conditions.push(eq(timesheets.year, parseInt(year)));
  }

  if (month) {
    conditions.push(eq(timesheets.month, parseInt(month)));
  }

  return db
    .select({
      id: timesheets.id,
      fileName: timesheets.fileName,
      fileType: timesheets.fileType,
      fileSize: timesheets.fileSize,
      year: timesheets.year,
      month: timesheets.month,
      isGenerated: timesheets.isGenerated,
      uploadedAt: timesheets.uploadedAt,
    })
    .from(timesheets)
    .where(and(...conditions))
    .orderBy(desc(timesheets.uploadedAt));
};

export const getAllTimesheets = async ({ userId, year, month } = {}) => {
  const conditions = [];

  if (userId) {
    conditions.push(eq(timesheets.userId, userId));
  }

  if (year) {
    conditions.push(eq(timesheets.year, parseInt(year)));
  }

  if (month) {
    conditions.push(eq(timesheets.month, parseInt(month)));
  }

  return db
    .select({
      id: timesheets.id,
      fileName: timesheets.fileName,
      fileType: timesheets.fileType,
      fileSize: timesheets.fileSize,
      year: timesheets.year,
      month: timesheets.month,
      isGenerated: timesheets.isGenerated,
      uploadedAt: timesheets.uploadedAt,
      userId: timesheets.userId,
      username: users.username,
      firstName: users.firstName,
      lastName: users.lastName,
    })
    .from(timesheets)
    .leftJoin(users, eq(timesheets.userId, users.id))
    .where(conditions.length > 0 ? and(...conditions) : undefined)
    .orderBy(desc(timesheets.uploadedAt));
};

export const getDownloadInfo = async (timesheetId, { userId, role }) => {
  const timesheet = await ensureTimesheetExists(timesheetId);
  assertAccess(timesheet, { userId, role });

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

  return {
    timesheet,
    filePath: timesheet.filePath,
    fileName: timesheet.fileName,
  };
};

export const deleteTimesheet = async (timesheetId, { userId, role }, auditContext = null) => {
  const timesheet = await ensureTimesheetExists(timesheetId);
  assertAccess(timesheet, { userId, role });

  await safeUnlink(timesheet.filePath);
  await db.delete(timesheets).where(eq(timesheets.id, timesheetId));

  if (auditContext) {
    await logTimesheetDeleted(auditContext.userId, timesheetId, timesheet.year, timesheet.month, auditContext.ipAddress, auditContext.userAgent);
  }
};
