const { Op } = require("sequelize");
const { Users, AssignReport, Reports } = require("../models");
const uploadFile = require("../utils/uploadFile");
const { addFormattedIds, addFormattedId } = require("../utils/idFormatter");
const {
  sendParticipantAssignmentEmail,
  sendDeviationAssignmentEmail,
  sendCaseInvestigationAssignmentEmail,
} = require("../utils/emailService");
const {
  createReportAssignmentNotification,
} = require("../utils/notificationHelper");
const { logActivity, getIpAddress, getUserAgent } = require("../utils/activityLogger");
const { syncToSharepoint } = require("../services/sharepoint");

// Map report types to entity types for ID formatting
const getEntityType = (reportType) => {
  const typeMap = {
    deviations: "deviations",
    "case-investigations": "case-investigations",
    "site-monitoring": "site-monitoring",
  };
  return typeMap[reportType] || reportType;
};

const getReports = async (req, res) => {
  const { type, projectId } = req.query;
  if (type === undefined)
    return res.status(400).json({ message: "Type is required" });

  try {
    const reports = await Reports.findAll({
      where: {
        type,
        ProjectId: projectId,
      },
      include: [
        {
          model: AssignReport,
        },
      ],
    });

    const formattedReports = reports.map(({ data, id, AssignReports }) => ({
      ...JSON.parse(data),
      id,
      status: ["Open", "In Progress", "Closed"].find((s) =>
        AssignReports.some((r) => r.status === s),
      ),
    }));

    // Add formatted IDs based on report type
    const entityType = getEntityType(type);
    res.json(addFormattedIds(entityType, formattedReports));
  } catch (err) {
    res.status(500).json({ message: "Failed to fetch" });
  }
};

const getReport = async (req, res) => {
  const { id } = req.params;
  const { type } = req.query;
  if (!id) return res.status(400).json({ message: "Id is required" });

  try {
    const report = await Reports.findByPk(id);
    const assignReport = await AssignReport.findAll({
      where: {
        ReportId: id,
      },
      include: [
        {
          model: Users,
          attributes: {
            exclude: ["password", "createdAt", "updatedAt"],
          },
        },
      ],
    });

    const fullnames = {};

    for (const i in assignReport) {
      if (Object.prototype.hasOwnProperty.call(assignReport, i)) {
        const item = assignReport[i];
        if (item.User && item) {
          fullnames[item.UserId] = {};
          fullnames[item.UserId].fullname = item.User.fullname;
          fullnames[item.UserId].status = item.status;
        }
      }
    }

    const data = {};

    for (const key in JSON.parse(report.data)) {
      if (Object.prototype.hasOwnProperty.call(JSON.parse(report.data), key)) {
        const value = JSON.parse(report.data)[key];
        if (key.includes("assign_to")) {
          const match = key.match(/--(\d+)/);
          if (fullnames[value]) {
            data[`status${match ? "--" + match[1] : ""}`] =
              fullnames[value].status;
            data[key] = {
              value: fullnames[value].fullname,
              id: value,
            };
            continue;
          }
        }
        data[key] = value;
      }
    }

    const responseData = {
      assignReport: assignReport.map(({ status, id, User: { fullname } }) => ({
        status,
        id,
        fullname,
      })),
      data: {
        ...data,
        id: report.id,
      },
    };

    // Add formatted ID if type is provided
    if (type) {
      const entityType = getEntityType(type);
      responseData.data = addFormattedId(entityType, responseData.data);
    }

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

const createReport = async (req, res) => {
  const { type, projectId } = req.query;
  if (!type) return res.status(400).json({ message: "Type is required" });
  if (!projectId)
    return res.status(400).json({ message: "Project id is required" });

  try {
    let data;
    if (req.files) {
      const filesData = await uploadFile(req.files);
      data = { ...JSON.parse(req.body.data), ...filesData };
    } else {
      data = req.body;
    }

    const createdReport = await Reports.create({
      type,
      data,
      ProjectId: projectId,
    });

    // Get project and assigned by user info for email
    const { Projects } = require("../models");
    const project = await Projects.findByPk(projectId);
    const assignedByUser = await Users.findByPk(req.user.id);

    // Get formatted ID for the report
    const entityType = getEntityType(type);
    const formattedReport = addFormattedId(entityType, {
      id: createdReport.id,
    });

    // Map report type to entity type for activity log
    const entityTypeMap = {
      "deviations": "Deviations",
      "case-investigations": "Case Investigation",
      "site-monitoring": "Site Monitoring",
    };
    const activityEntityType = entityTypeMap[type] || type;

    // Log report creation activity
    await logActivity({
      userId: req.user.id,
      companyId: req.user.companyId,
      projectId: projectId,
      action: "create",
      entityType: activityEntityType,
      entityId: createdReport.id,
      description: `New ${activityEntityType.toLowerCase()} report created`,
      metadata: {
        reportId: formattedReport.formattedId,
        reportType: type,
      },
      ipAddress: getIpAddress(req),
      userAgent: getUserAgent(req),
    });

    // Sync to SharePoint
    try {
      const sharepointFields = {
        Title: data.title || `${type} Report`,
        Status: "Open",
        Description: data.description || "",
        ProjectId: projectId.toString(),
      };

      // Add type-specific fields
      if (type === "deviations") {
        sharepointFields.Severity = data.severity || "";
        sharepointFields.Location = data.location || "";
        sharepointFields.ActionTaken = data.actionTaken || "";
        sharepointFields.RootCause = data.rootCause || "";
        sharepointFields.ReportedBy = assignedByUser ? assignedByUser.fullname : "";
        sharepointFields.DeviationDate = data.dateObserved || new Date().toISOString();
      } else if (type === "site-monitoring") {
        sharepointFields.RiskLevel = data.riskLevel || "";
        sharepointFields.Location = data.location || "";
        sharepointFields.CorrectiveAction = data.correctiveAction || "";
        sharepointFields.Priority = data.priority || "";
        sharepointFields.MonitoredBy = assignedByUser ? assignedByUser.fullname : "";
        sharepointFields.MonitoringDate = data.dateObserved || new Date().toISOString();
      } else if (type === "case-investigations") {
        sharepointFields.Severity = data.severity || "";
        sharepointFields.Location = data.location || "";
        sharepointFields.ReportedBy = assignedByUser ? assignedByUser.fullname : "";
        sharepointFields.IncidentDate = data.incidentDate || new Date().toISOString();
      }

      await syncToSharepoint({
        companyId: req.user.companyId,
        recordType: type,
        recordId: createdReport.id,
        fields: sharepointFields,
      });

      console.log(`✅ Report synced to SharePoint: ${type} ID ${createdReport.id}`);
    } catch (sharepointError) {
      console.error(`SharePoint sync failed for ${type}:`, sharepointError.message);
      // Don't fail the report creation if SharePoint sync fails
    }

    // handle assign_to fields and send emails
    for (const key in data) {
      if (key.includes("assign_to")) {
        await AssignReport.create({
          ReportId: createdReport.id,
          UserId: data[key],
        });

        // Get assigned user info
        const assignedUser = await Users.findByPk(data[key]);
        if (!assignedUser) continue; // Skip if user not found

        // Calculate deadline (3 weeks from now)
        const deadline = new Date();
        deadline.setDate(deadline.getDate() + 21);

        // Extract severity/priority from report data
        let notificationPriority = "medium"; // default

        // Check for severity field in the data
        if (data.severity) {
          // Map severity to priority (severity values: low, medium, high, critical)
          const severityToPriority = {
            low: "low",
            medium: "medium",
            high: "high",
            critical: "urgent",
            urgent: "urgent",
          };
          notificationPriority =
            severityToPriority[data.severity.toLowerCase()] || "medium";
        } else if (data.risk_severity) {
          // For deviations (hazard reports) - check risk_severity field
          const severityToPriority = {
            low: "low",
            medium: "medium",
            high: "high",
            critical: "urgent",
            urgent: "urgent",
          };
          notificationPriority =
            severityToPriority[data.risk_severity.toLowerCase()] || "medium";
        } else if (data.priority) {
          // If priority field exists directly
          notificationPriority = data.priority.toLowerCase();
        }

        // Send assignment email and create notification
        try {
          if (assignedUser.email) {

            // Use specific email based on report type
            if (type === "deviations") {
              await sendDeviationAssignmentEmail({
                to: assignedUser.email,
                participantName: assignedUser.fullname,
                projectName: project ? project.name : "Unknown Project",
                deviationId: formattedReport.formattedId,
                assignedBy: assignedByUser ? assignedByUser.fullname : "System",
                preparedDate: new Date(),
                deadlineDate: deadline,
                priority: notificationPriority, // Pass the priority to email
              });
            } else if (type === "case-investigations") {
              await sendCaseInvestigationAssignmentEmail({
                to: assignedUser.email,
                participantName: assignedUser.fullname,
                projectName: project ? project.name : "Unknown Project",
                caseId: formattedReport.formattedId,
                assignedBy: assignedByUser ? assignedByUser.fullname : "System",
                preparedDate: new Date(),
                deadlineDate: deadline,
                priority: notificationPriority, // Pass the priority to email
              });
            } else {
              // Generic email for other report types
              await sendParticipantAssignmentEmail({
                to: assignedUser.email,
                participantName: assignedUser.fullname,
                projectName: project ? project.name : "Unknown Project",
                assignedBy: assignedByUser ? assignedByUser.fullname : "System",
                preparedDate: new Date(),
                deadlineDate: deadline,
                reportType: type
                  .replace(/-/g, " ")
                  .replace(/\b\w/g, (l) => l.toUpperCase()),
                reportId: formattedReport.formattedId,
                priority: notificationPriority, // Pass the priority to email
              });
            }
          }

          // Create notification for the assigned user (ONLY ONCE)
          await createReportAssignmentNotification({
            userId: data[key],
            assignedBy: req.user.id,
            companyId: req.user.companyId,
            projectId: projectId,
            reportType: type,
            reportId: createdReport.id,
            formattedId: formattedReport.formattedId,
            priority: notificationPriority,
            dueDate: deadline,
          });
        } catch (emailOrNotificationError) {
          console.error("Failed to send email or create notification:", emailOrNotificationError);
          // Don't fail the report creation if email/notification fails
        }

        // Log assignment activity (outside email try-catch)
        try {
          await logActivity({
            userId: assignedUser.id, // Use the assigned user's ID
            companyId: req.user.companyId,
            projectId: projectId,
            action: "assign",
            entityType: activityEntityType,
            entityId: createdReport.id,
            description: `Assigned to handle ${activityEntityType.toLowerCase()} report`,
            metadata: {
              reportId: formattedReport.formattedId,
              reportType: type,
              assignedBy: assignedByUser ? assignedByUser.fullname : "System",
              assignedById: req.user.id,
              priority: notificationPriority,
            },
            ipAddress: getIpAddress(req),
            userAgent: getUserAgent(req),
          });
        } catch (activityError) {
          console.error("Failed to log assignment activity:", activityError);
        }
      }
    }

    res.status(201).json({ message: "Report created successfully" });
  } catch (err) {
    console.error(err);
    res.status(500).json({ message: err.message || "Failed to create" });
  }
};

const updateReport = async (req, res) => {
  const { projectId, editId } = req.query;
  if (!projectId)
    return res.status(400).json({ message: "Project id is required" });
  if (!editId) return res.status(400).json({ message: "Edit id is required" });

  try {
    let data;
    if (req.files) {
      const filesData = await uploadFile(req.files);
      data = { ...JSON.parse(req.body.data), ...filesData };
    } else {
      data = req.body;
    }

    const report = await Reports.findByPk(editId);
    if (!report) return res.status(404).json({ message: "Report not found" });

    const assignReports = await AssignReport.findAll({
      where: {
        ReportId: editId,
      },
    });

    const assignArr = [];

    for (const key in data) {
      if (Object.prototype.hasOwnProperty.call(data, key)) {
        const value = data[key];
        if (key.includes("assign_to")) {
          assignArr.push({
            ReportId: report.id,
            UserId: value,
          });
        }
      }
    }

    const existingUserIds = assignReports.map((report) => report.UserId);
    const newUserIds = assignArr.map((report) => report.UserId);

    const usersToRemove = existingUserIds.filter(
      (id) => !newUserIds.includes(id),
    );
    const usersToAdd = newUserIds.filter((id) => !existingUserIds.includes(id));

    if (usersToRemove.length > 0)
      await AssignReport.destroy({
        where: {
          ReportId: report.id,
          UserId: {
            [Op.in]: usersToRemove,
          },
        },
      });

    await AssignReport.bulkCreate(
      usersToAdd.map((UserId) => ({
        ReportId: report.id,
        UserId,
      })),
    );

    await Reports.update(
      {
        data,
        ProjectId: projectId,
      },
      {
        where: {
          id: editId,
        },
      },
    );

    // Get formatted ID for the report
    const entityType = getEntityType(report.type);
    const formattedReport = addFormattedId(entityType, {
      id: report.id,
    });

    // Map report type to entity type for activity log
    const entityTypeMap = {
      "deviations": "Deviations",
      "case-investigations": "Case Investigation",
      "site-monitoring": "Site Monitoring",
    };
    const activityEntityType = entityTypeMap[report.type] || report.type;

    // Log report edit activity
    await logActivity({
      userId: req.user.id,
      companyId: req.user.companyId,
      projectId: projectId,
      action: "edit",
      entityType: activityEntityType,
      entityId: report.id,
      description: `${activityEntityType} report updated`,
      metadata: {
        reportId: formattedReport.formattedId,
        reportType: report.type,
      },
      ipAddress: getIpAddress(req),
      userAgent: getUserAgent(req),
    });

    res.status(201).json({ message: "Report updated successfully" });
  } catch (err) {
    res.status(500).json({ message: "Failed to create" });
  }
};

const updateReportStatus = async (req, res) => {
  try {
    if (!Array.isArray(req.body))
      return res.status(400).json({ message: "Something went wrong" });

    for (let i = 0; i < req.body.length; i++) {
      const { status, id } = req.body[i];

      // Get the current assignment to check old status
      const assignReport = await AssignReport.findByPk(id, {
        include: [
          {
            model: Reports,
            attributes: ["id", "type", "ProjectId"],
          },
        ],
      });

      if (assignReport) {
        const oldStatus = assignReport.status;
        const newStatus = status;

        // Update status
        await AssignReport.update(
          {
            status,
          },
          {
            where: {
              id,
            },
          },
        );

        // Log status change activity (only if status actually changed)
        if (oldStatus !== newStatus && assignReport.Report) {
          const report = assignReport.Report;
          
          // Get formatted ID for the report
          const entityType = getEntityType(report.type);
          const formattedReport = addFormattedId(entityType, {
            id: report.id,
          });

          // Map report type to entity type for activity log
          const entityTypeMap = {
            "deviations": "Deviations",
            "case-investigations": "Case Investigation",
            "site-monitoring": "Site Monitoring",
          };
          const activityEntityType = entityTypeMap[report.type] || report.type;

          await logActivity({
            userId: req.user.id,
            companyId: req.user.companyId,
            projectId: report.ProjectId,
            action: "status_change",
            entityType: activityEntityType,
            entityId: report.id,
            description: `${activityEntityType} status changed from ${oldStatus} to ${newStatus}`,
            metadata: {
              reportId: formattedReport.formattedId,
              reportType: report.type,
              statusChange: {
                from: oldStatus,
                to: newStatus,
              },
            },
            ipAddress: getIpAddress(req),
            userAgent: getUserAgent(req),
          });
        }
      }
    }

    res.status(201).json({ message: "Report updated successfully" });
  } catch (err) {
    console.error("Error updating report status:", err);
    res.status(500).json({ message: "Failed to create" });
  }
};

module.exports = {
  getReports,
  createReport,
  getReport,
  updateReportStatus,
  updateReport,
};
