const { Op } = require("sequelize");
const { Users, EntranceLog, Projects } = require("../models");

function hoursWorked(inTime, outTime) {
  if (!inTime || !outTime) return null;
  const ms = new Date(outTime).getTime() - new Date(inTime).getTime();
  if (ms < 0) return null;
  return Math.round((ms / 3600000) * 100) / 100;
}

function mapLogRow(log) {
  const j = log.toJSON ? log.toJSON() : log;
  const u = j.User;
  const p = j.Project;
  const inTime = j.inTime;
  const outTime = j.outTime;
  const status = outTime ? "Checked out" : "On site";
  return {
    id: j.id,
    UserId: j.UserId,
    ProjectId: j.ProjectId,
    inTime,
    outTime,
    extraTime: j.extraTime,
    fullname: u ? u.fullname : null,
    email: u ? u.email : null,
    projectName: p ? p.name : null,
    hoursWorked: hoursWorked(inTime, outTime),
    status,
  };
}

const getEntrancesLogs = async (req, res) => {
  const { projectId, scope, employeeName, startDate, endDate } = req.query;

  try {
    const myScope =
      scope === "my" ||
      (req.user?.role && ["user", "visitor"].includes(req.user.role));

    if (myScope) {
      if (!projectId)
        return res.status(400).json({ message: "projectId is required" });
      const exists = await Projects.findByPk(projectId);
      if (!exists) return res.status(404).json({ message: "Project not found" });

      const entranceLogs = await EntranceLog.findAll({
        where: {
          ProjectId: projectId,
          UserId: req.user.id,
        },
        include: [
          {
            model: Users,
            attributes: { exclude: ["password"] },
          },
        ],
        order: [["inTime", "DESC"]],
      });

      return res.json(
        entranceLogs.map((log) => ({
          ...mapLogRow(log),
          fullname: log.User?.fullname,
        })),
      );
    }

    const canSeeAll = ["hr", "admin", "super-admin"].includes(req.user?.role);
    if (!canSeeAll) return res.status(403).json({ message: "Forbidden" });

    const companyProjects = await Projects.findAll({
      where: { CompanyId: req.user.companyId },
      attributes: ["id"],
    });
    const projectIds = companyProjects.map((p) => p.id);
    if (!projectIds.length) return res.json([]);

    const userInclude = {
      model: Users,
      attributes: { exclude: ["password"] },
      required: !!(employeeName && String(employeeName).trim()),
    };
    if (employeeName && String(employeeName).trim()) {
      userInclude.where = {
        fullname: { [Op.like]: `%${String(employeeName).trim()}%` },
      };
    }

    let entranceLogs = await EntranceLog.findAll({
      where: {
        ProjectId: { [Op.in]: projectIds },
      },
      include: [
        userInclude,
        { model: Projects, attributes: ["id", "name", "CompanyId"] },
      ],
      order: [["inTime", "DESC"]],
    });

    if (startDate) {
      const s = new Date(startDate);
      entranceLogs = entranceLogs.filter((l) => new Date(l.inTime) >= s);
    }
    if (endDate) {
      const e = new Date(endDate);
      e.setHours(23, 59, 59, 999);
      entranceLogs = entranceLogs.filter((l) => new Date(l.inTime) <= e);
    }

    res.json(entranceLogs.map(mapLogRow));
  } catch (err) {
    res.status(500).json({ message: "Failed to fetch", err: err.message });
  }
};

const addEntranceLog = async (req, res) => {
  const { ProjectId } = req.body;

  try {
    const exists = await Projects.findByPk(ProjectId);

    if (!exists) return res.status(404).json({ message: "Project not found" });

    const entrancesLogs = await EntranceLog.findAll({
      where: {
        ProjectId,
        UserId: req.user.id,
      },
    });

    if (entrancesLogs.length < 1) {
      await EntranceLog.create({
        ProjectId,
        UserId: req.user.id,
        inTime: new Date(),
      });
      return res.json({ message: "Your entrance log signed" });
    }
    const lastLog = entrancesLogs[entrancesLogs.length - 1];
    const interval = new Date().getTime() - new Date(lastLog.inTime).getTime();

    if (interval < 60 * 60 * 1000)
      return res.status(400).json({
        message: "You have already signed in recently",
      });

    if (interval > 12 * 60 * 60 * 1000 || lastLog.outTime) {
      await EntranceLog.create({
        ProjectId,
        UserId: req.user.id,
        inTime: new Date(),
      });
      return res.json({ message: "Your entrance log signed" });
    }

    await EntranceLog.update(
      {
        outTime: new Date(),
      },
      {
        where: {
          id: lastLog.id,
        },
      }
    );

    res.json({ message: "Your entrance log signed" });
  } catch (err) {
    res.status(500).json({ message: "Failed to fetch", err: err.message });
  }
};

const updateEntranceLog = async (req, res) => {
  const permissions = ["admin", "super-admin", "hr"];
  if (!permissions.includes(req.user?.role))
    return res.status(403).json({ message: "Forbidden" });

  const { id, time, type } = req.body;
  if (!id) return res.status(400).json({ message: "Missing entrance log ID" });
  if (!time) return res.status(400).json({ message: "Missing time" });
  if (!type) return res.status(400).json({ message: "Missing type" });

  try {
    const entranceLogs = await EntranceLog.findByPk(id, { include: [Projects] });
    if (!entranceLogs)
      return res.status(404).json({ message: "Entrance log not found" });

    if (
      req.user.role === "hr" &&
      entranceLogs.Project?.CompanyId !== req.user.companyId
    ) {
      return res.status(403).json({ message: "Forbidden" });
    }

    await EntranceLog.update(
      {
        [type == "extra" ? "extraTime" : "outTime"]: new Date(time),
      },
      {
        where: {
          id,
        },
      }
    );

    res.json({ message: "Entrance log updated successfully" });
  } catch (err) {
    res.status(500).json({ message: "Failed to fetch", err: err.message });
  }
};

module.exports = { getEntrancesLogs, addEntranceLog, updateEntranceLog };
