const { Op } = require("sequelize");
const { LeaveRequest, Users } = require("../models");
const { LEAVE_TYPES } = require("../models/leave-request");
const {
  createLeaveRequestNotification,
  createLeaveResponseNotification,
} = require("../utils/notificationHelper");

function dayDiff(start, end) {
  const a = new Date(start);
  const b = new Date(end);
  return Math.max(1, Math.round((b - a) / (24 * 60 * 60 * 1000)) + 1);
}

const createLeaveRequest = async (req, res) => {
  const { type, startDate, endDate, comment } = req.body;
  if (!type || !LEAVE_TYPES.includes(type)) {
    return res.status(400).json({ message: "Valid leave type is required" });
  }
  if (!startDate || !endDate) {
    return res.status(400).json({ message: "startDate and endDate are required" });
  }
  if (new Date(startDate) > new Date(endDate)) {
    return res.status(400).json({ message: "startDate must be before or equal to endDate" });
  }

  try {
    const employee = await Users.findByPk(req.user.id);
    if (!employee) return res.status(404).json({ message: "User not found" });

    const row = await LeaveRequest.create({
      UserId: req.user.id,
      CompanyId: req.user.companyId,
      type,
      startDate,
      endDate,
      comment: comment || null,
      status: "pending",
    });

    await createLeaveRequestNotification({
      leaveRequestId: row.id,
      employeeId: employee.id,
      employeeName: employee.fullname,
      companyId: req.user.companyId,
      leaveType: type,
      startDate: row.startDate,
      endDate: row.endDate,
    });

    res.status(201).json(row);
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to create leave request" });
  }
};

const getMyLeaveRequests = async (req, res) => {
  try {
    const rows = await LeaveRequest.findAll({
      where: { UserId: req.user.id },
      include: [
        { model: Users, as: "Reviewer", attributes: ["id", "fullname", "email"], required: false },
      ],
      order: [["createdAt", "DESC"]],
    });
    res.json(rows.map(formatLeaveRow));
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to fetch leave requests" });
  }
};

const getCompanyLeaveRequests = async (req, res) => {
  try {
    const { status: st, employeeId } = req.query;
    const where = { CompanyId: req.user.companyId };
    if (st && ["pending", "approved", "rejected"].includes(st)) where.status = st;
    if (employeeId) where.UserId = employeeId;

    const rows = await LeaveRequest.findAll({
      where,
      include: [
        { model: Users, attributes: ["id", "fullname", "email", "role", "department"], required: true },
        { model: Users, as: "Reviewer", attributes: ["id", "fullname"], required: false },
      ],
      order: [["createdAt", "DESC"]],
    });
    res.json(rows.map(formatLeaveRow));
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to fetch leave requests" });
  }
};

const getPendingLeaveRequests = async (req, res) => {
  try {
    const rows = await LeaveRequest.findAll({
      where: { CompanyId: req.user.companyId, status: "pending" },
      include: [
        { model: Users, attributes: ["id", "fullname", "email", "role", "department"], required: true },
      ],
      order: [["createdAt", "ASC"]],
    });
    res.json(rows.map(formatLeaveRow));
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to fetch pending requests" });
  }
};

const approveLeaveRequest = async (req, res) => {
  const { id } = req.params;
  try {
    const row = await LeaveRequest.findOne({
      where: { id, CompanyId: req.user.companyId, status: "pending" },
    });
    if (!row) return res.status(404).json({ message: "Leave request not found or not pending" });

    await row.update({
      status: "approved",
      ReviewedById: req.user.id,
      reviewNote: null,
      reviewedAt: new Date(),
    });

    const emp = await Users.findByPk(row.UserId);
    await createLeaveResponseNotification({
      leaveRequestId: row.id,
      employeeId: row.UserId,
      reviewerId: req.user.id,
      companyId: row.CompanyId,
      status: "approved",
      reviewNote: null,
      leaveType: row.type,
      startDate: row.startDate,
      endDate: row.endDate,
    });

    res.json({ message: "Leave request approved", leaveRequest: await reloadLeave(row.id) });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to approve" });
  }
};

const rejectLeaveRequest = async (req, res) => {
  const { id } = req.params;
  const { reviewNote } = req.body;
  try {
    const row = await LeaveRequest.findOne({
      where: { id, CompanyId: req.user.companyId, status: "pending" },
    });
    if (!row) return res.status(404).json({ message: "Leave request not found or not pending" });

    await row.update({
      status: "rejected",
      ReviewedById: req.user.id,
      reviewNote: reviewNote || null,
      reviewedAt: new Date(),
    });

    await createLeaveResponseNotification({
      leaveRequestId: row.id,
      employeeId: row.UserId,
      reviewerId: req.user.id,
      companyId: row.CompanyId,
      status: "rejected",
      reviewNote: reviewNote || "",
      leaveType: row.type,
      startDate: row.startDate,
      endDate: row.endDate,
    });

    res.json({ message: "Leave request rejected", leaveRequest: await reloadLeave(row.id) });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: "Failed to reject" });
  }
};

async function reloadLeave(id) {
  const row = await LeaveRequest.findByPk(id, {
    include: [
      { model: Users, attributes: ["id", "fullname", "email", "role", "department"] },
      { model: Users, as: "Reviewer", attributes: ["id", "fullname"], required: false },
    ],
  });
  return formatLeaveRow(row);
}

function formatLeaveRow(row) {
  const j = row.toJSON ? row.toJSON() : row;
  const emp = j.User || null;
  const days = dayDiff(j.startDate, j.endDate);
  return {
    id: j.id,
    userId: j.UserId,
    companyId: j.CompanyId,
    type: j.type,
    startDate: j.startDate,
    endDate: j.endDate,
    days,
    comment: j.comment,
    status: j.status,
    reviewNote: j.reviewNote,
    reviewedAt: j.reviewedAt,
    reviewedById: j.ReviewedById,
    createdAt: j.createdAt,
    updatedAt: j.updatedAt,
    employee:
      emp && emp.fullname
        ? {
            id: emp.id,
            fullname: emp.fullname,
            email: emp.email,
            role: emp.role,
            department: emp.department,
          }
        : null,
    reviewer: j.Reviewer ? { id: j.Reviewer.id, fullname: j.Reviewer.fullname } : null,
  };
}

module.exports = {
  createLeaveRequest,
  getMyLeaveRequests,
  getCompanyLeaveRequests,
  getPendingLeaveRequests,
  approveLeaveRequest,
  rejectLeaveRequest,
};
