const { HazopStudy, HazopAction, Projects, Users } = require("../models");
const { addFormattedIds, addFormattedId, formatId } = require("../utils/idFormatter");
const {
  generateNodes,
  analyzeNode,
  generateHazards,
  chatWithAI,
} = require("../services/hazop-ai-service");
const { sendHazopActionAssignmentEmail } = require("../utils/emailService");
const {
  logActivity,
  getActivityDescription,
  getIpAddress,
  getUserAgent,
} = require("../utils/activityLogger");
const { Op, literal } = require("sequelize");
const sequelize = require("../db");

/** Allowed filter values — never interpolate raw query params into SQL */
const HAZOP_STUDY_TYPES = new Set(["hazop", "hazid"]);
const HAZOP_STATUSES = new Set(["draft", "in-progress", "approved", "closed"]);
const HAZOP_PROCESS_TYPES = new Set([
  "oil-gas",
  "chemical",
  "mining",
  "power",
  "pharma",
  "construction",
]);

/**
 * Get all HAZOP/HAZID studies for a project
 * GET /api/hazop/studies?projectId={id}&studyType={type}&status={status}&processType={type}
 */
const getStudies = async (req, res) => {
  try {
    const { projectId, studyType, status, processType } = req.query;

    if (!projectId) {
      return res.status(400).json({
        success: false,
        message: "projectId is required",
      });
    }

    // Build where clause for filtering
    const where = { ProjectId: projectId };

    // Apply filters using JSON path queries (values validated against allowlists)
    if (studyType && studyType !== "all") {
      if (!HAZOP_STUDY_TYPES.has(studyType)) {
        return res.status(400).json({
          success: false,
          message: "Invalid studyType filter",
        });
      }
      where[Op.and] = where[Op.and] || [];
      where[Op.and].push(
        literal(
          `JSON_UNQUOTE(JSON_EXTRACT(\`data\`, '$.studyType')) = ${sequelize.escape(studyType)}`,
        ),
      );
    }
    if (status && status !== "all") {
      if (!HAZOP_STATUSES.has(status)) {
        return res.status(400).json({
          success: false,
          message: "Invalid status filter",
        });
      }
      where[Op.and] = where[Op.and] || [];
      where[Op.and].push(
        literal(
          `JSON_UNQUOTE(JSON_EXTRACT(\`data\`, '$.status')) = ${sequelize.escape(status)}`,
        ),
      );
    }
    if (processType && processType !== "all") {
      if (!HAZOP_PROCESS_TYPES.has(processType)) {
        return res.status(400).json({
          success: false,
          message: "Invalid processType filter",
        });
      }
      where[Op.and] = where[Op.and] || [];
      where[Op.and].push(
        literal(
          `JSON_UNQUOTE(JSON_EXTRACT(\`data\`, '$.processType')) = ${sequelize.escape(processType)}`,
        ),
      );
    }

    // Fetch studies with filters
    const studies = await HazopStudy.findAll({
      where,
      include: [
        {
          model: Users,
          as: "CreatedBy",
          attributes: ["id", "fullname", "email"],
        },
      ],
      order: [["createdAt", "DESC"]],
    });

    // Add formatted IDs and parse data
    const studiesWithIds = studies.map((study) => {
      // Parse the data field if it's a string
      let parsedData = study.data;
      if (typeof study.data === 'string') {
        try {
          parsedData = JSON.parse(study.data);
        } catch (err) {
          console.error("❌ Failed to parse data for study", study.id, ":", err);
          parsedData = {};
        }
      }

      // Manually construct the object
      const studyData = {
        id: study.id,
        data: parsedData, // This is now guaranteed to be an object
        ProjectId: study.ProjectId,
        createdById: study.createdById,
        createdAt: study.createdAt,
        updatedAt: study.updatedAt,
        CreatedBy: study.CreatedBy ? {
          id: study.CreatedBy.id,
          fullname: study.CreatedBy.fullname,
          email: study.CreatedBy.email,
        } : null,
      };
      
      const result = {
        ...studyData,
        formattedId: `HAZ-${String(studyData.id).padStart(3, '0')}`,
      };
      
      return result;
    });

    res.json(studiesWithIds);
  } catch (err) {
    console.error("Error fetching studies:", err);
    res.status(500).json({
      success: false,
      message: "Failed to fetch studies",
      error: err.message,
    });
  }
};

/**
 * Get single HAZOP/HAZID study with all actions
 * GET /api/hazop/studies/:id
 */
const getSingleStudy = async (req, res) => {
  try {
    const { id } = req.params;

    const study = await HazopStudy.findOne({
      where: { id },
      include: [
        {
          model: Users,
          as: "CreatedBy",
          attributes: ["id", "fullname", "email"],
        },
      ],
    });

    console.log("🔍 Study found:", !!study);
    console.log("🔍 Study constructor:", study?.constructor?.name);
    console.log("🔍 Study keys:", study ? Object.keys(study).slice(0, 10) : []);

    if (!study) {
      console.log("❌ Study not found with ID:", id);
      return res.status(404).json({
        success: false,
        message: "Study not found",
      });
    }

    // Fetch all actions for this study
    const actions = await HazopAction.findAll({
      where: { HazopStudyId: id },
      include: [
        {
          model: Users,
          as: "Owner",
          attributes: ["id", "fullname", "email"],
        },
        {
          model: Users,
          as: "ClosedBy",
          attributes: ["id", "fullname", "email"],
        },
      ],
      order: [["createdAt", "DESC"]],
    });

    // Parse the data field if it's a string
    let parsedData = study.data;
    if (typeof study.data === 'string') {
      try {
        parsedData = JSON.parse(study.data);
      } catch (err) {
        console.error("❌ Failed to parse study data:", err);
        parsedData = {};
      }
    }

    // Manually construct the response object
    const studyData = {
      id: study.id,
      data: parsedData,
      ProjectId: study.ProjectId,
      createdById: study.createdById,
      createdAt: study.createdAt,
      updatedAt: study.updatedAt,
      CreatedBy: study.CreatedBy ? {
        id: study.CreatedBy.id,
        fullname: study.CreatedBy.fullname,
        email: study.CreatedBy.email,
      } : null,
    };
    
    const prefix = "HAZ";

    const actionsData = actions.map((action) => {
      // Parse action data if it's a string
      let parsedActionData = action.data;
      if (typeof action.data === 'string') {
        try {
          parsedActionData = JSON.parse(action.data);
        } catch (err) {
          console.error("❌ Failed to parse action data:", err);
          parsedActionData = {};
        }
      }
      
      return {
        id: action.id,
        data: parsedActionData,
        status: action.status,
        dueDate: action.dueDate,
        closedAt: action.closedAt,
        HazopStudyId: action.HazopStudyId,
        ownerId: action.ownerId,
        closedById: action.closedById,
        createdAt: action.createdAt,
        updatedAt: action.updatedAt,
        Owner: action.Owner ? {
          id: action.Owner.id,
          fullname: action.Owner.fullname,
          email: action.Owner.email,
        } : null,
        ClosedBy: action.ClosedBy ? {
          id: action.ClosedBy.id,
          fullname: action.ClosedBy.fullname,
          email: action.ClosedBy.email,
        } : null,
      };
    });

    res.json({
      study: {
        ...studyData,
        formattedId: `HAZ-${String(studyData.id).padStart(3, '0')}`,
      },
      actions: actionsData,
    });
  } catch (err) {
    console.error("❌ Error fetching study:", err);
    console.error("❌ Error stack:", err.stack);
    res.status(500).json({
      success: false,
      message: "Failed to fetch study",
      error: err.message,
    });
  }
};

/**
 * Create new HAZOP/HAZID study with actions
 * POST /api/hazop/studies?projectId={id}
 */
const createStudy = async (req, res) => {
  const transaction = await HazopStudy.sequelize.transaction();

  try {
    const { projectId } = req.query;
    const { studyData, actions } = req.body;

    // Debug logging
    console.log("🔍 Received projectId from query:", projectId);
    console.log("🔍 ProjectId type:", typeof projectId);
    console.log("🔍 Full query params:", req.query);

    if (!projectId) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: "projectId is required",
      });
    }

    // Validate projectId is a valid number
    const projectIdNum = parseInt(projectId, 10);
    if (isNaN(projectIdNum)) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: `Invalid projectId: ${projectId}. Must be a valid number.`,
      });
    }

    if (!studyData) {
      await transaction.rollback();
      return res.status(400).json({
        success: false,
        message: "studyData is required",
      });
    }

    // Verify project exists
    const project = await Projects.findByPk(projectIdNum);
    if (!project) {
      await transaction.rollback();
      return res.status(404).json({
        success: false,
        message: "Project not found",
      });
    }

    // Create the study
    const study = await HazopStudy.create(
      {
        data: studyData,
        ProjectId: projectIdNum,
        createdById: req.user.id,
      },
      { transaction }
    );

    // Create actions if provided
    const createdActions = [];
    if (actions && Array.isArray(actions) && actions.length > 0) {
      for (const actionData of actions) {
        const action = await HazopAction.create(
          {
            data: {
              findingRef: actionData.findingRef,
              description: actionData.description,
              nodeNumber: actionData.nodeNumber,
              parameter: actionData.parameter,
              guideword: actionData.guideword,
              category: actionData.category,
              riskScore: actionData.riskScore,
              riskLevel: actionData.riskLevel,
            },
            status: "open",
            dueDate: actionData.dueDate,
            HazopStudyId: study.id,
            ownerId: actionData.ownerId,
          },
          { transaction }
        );
        createdActions.push(action);
      }
    }

    await transaction.commit();

    // After commit, handle non-critical operations (logging, emails)
    // These operations should not cause the request to fail
    let studyJson;
    let formattedStudyId;
    
    try {
      // Fetch the created study with relationships
      const createdStudy = await HazopStudy.findByPk(study.id, {
        include: [
          {
            model: Users,
            as: "CreatedBy",
            attributes: ["id", "fullname", "email"],
          },
        ],
      });

      if (!createdStudy) {
        console.error("⚠️ Could not fetch created study, using original study object");
        studyJson = study.toJSON();
      } else {
        studyJson = createdStudy.toJSON();
      }
      
      const prefix = studyJson.data.studyType === "hazop" ? "HZP" : "HZD";
      formattedStudyId = addFormattedId(prefix, studyJson.id);

      // Log study creation activity
      await logActivity({
        userId: req.user.id,
        projectId: projectIdNum,
        action: "create",
        entityType: studyJson.data.studyType === "hazop" ? "HAZOP Study" : "HAZID Study",
        entityId: study.id,
        description: getActivityDescription("create", studyJson.data.studyType === "hazop" ? "HAZOP Study" : "HAZID Study", {
          details: `${formattedStudyId} - ${studyJson.data.title}`,
        }),
        metadata: {
          studyType: studyJson.data.studyType,
          processType: studyJson.data.processType,
          facilityName: studyJson.data.facilityName,
          formattedId: formattedStudyId,
        },
        ipAddress: getIpAddress(req),
        userAgent: getUserAgent(req),
      });

      // Log action assignment activities
      if (createdActions.length > 0) {
        for (const action of createdActions) {
          await logActivity({
            userId: req.user.id,
            projectId: projectIdNum,
            action: "assign",
            entityType: "HAZOP Action",
            entityId: action.id,
            description: getActivityDescription("assign", "HAZOP Action", {
              details: `${action.data.findingRef} to user ${action.ownerId}`,
            }),
            metadata: {
              studyId: study.id,
              formattedStudyId: formattedStudyId,
              findingRef: action.data.findingRef,
              ownerId: action.ownerId,
              riskLevel: action.data.riskLevel,
              dueDate: action.dueDate,
            },
            ipAddress: getIpAddress(req),
            userAgent: getUserAgent(req),
          });
        }
      }

      // Send email notifications to action owners asynchronously
      if (createdActions.length > 0) {
        // Get base URL from environment or construct from request
        const baseUrl = process.env.FRONTEND_URL || `${req.protocol}://${req.get('host')}`;
        
        // Send emails in the background (don't await)
        Promise.all(
          createdActions.map(async (action) => {
            try {
              // Fetch owner details
              const owner = await Users.findByPk(action.ownerId, {
                attributes: ["id", "fullname", "email"],
              });

              if (owner && owner.email) {
                await sendHazopActionAssignmentEmail({
                  to: owner.email,
                  ownerName: owner.fullname,
                  studyTitle: studyJson.data.title,
                  studyType: studyJson.data.studyType,
                  studyId: formattedStudyId,
                  findingRef: action.data.findingRef,
                  description: action.data.description,
                  riskLevel: action.data.riskLevel,
                  dueDate: action.dueDate,
                  baseUrl: baseUrl,
                });
                console.log(`✅ Email sent to ${owner.email} for action ${action.data.findingRef}`);
              }
            } catch (emailError) {
              // Log email errors but don't fail the request
              console.error(`❌ Failed to send email for action ${action.id}:`, emailError.message);
            }
          })
        ).catch((err) => {
          console.error("❌ Error sending action assignment emails:", err);
        });
      }
    } catch (postCommitError) {
      // Log the error but don't fail the request since the study was created successfully
      console.error("⚠️ Error in post-commit operations:", postCommitError);
      console.error("⚠️ Study was created successfully, but logging/emails may have failed");
      
      // Ensure we have fallback values for the response
      if (!studyJson) {
        studyJson = study.toJSON();
      }
      if (!formattedStudyId) {
        const prefix = studyJson.data.studyType === "hazop" ? "HZP" : "HZD";
        formattedStudyId = addFormattedId(prefix, studyJson.id);
      }
    }

    res.status(201).json({
      success: true,
      study: {
        ...studyJson,
        formattedId: formattedStudyId,
      },
      actions: createdActions.map((action) => action.toJSON()),
      message: "Study created successfully",
    });
  } catch (err) {
    // Only rollback if transaction hasn't been committed yet
    if (!transaction.finished) {
      await transaction.rollback();
    }
    console.error("❌ Error creating study:", err);
    console.error("❌ Error stack:", err.stack);
    res.status(500).json({
      success: false,
      message: "Failed to create study",
      error: err.message,
    });
  }
};

/**
 * Get dashboard statistics
 * GET /api/hazop/dashboard?projectId={id}
 */
const getDashboard = async (req, res) => {
  try {
    const { projectId } = req.query;

    if (!projectId) {
      return res.status(400).json({
        success: false,
        message: "projectId is required",
      });
    }

    // Get all studies for the project
    const studies = await HazopStudy.findAll({
      where: { ProjectId: projectId },
    });

    // Get all actions for studies in this project
    const studyIds = studies.map((s) => s.id);
    if (studyIds.length === 0) {
      return res.json({
        success: true,
        statistics: {
          totalStudies: 0,
          hazopStudies: 0,
          hazidStudies: 0,
          overdueActions: 0,
          openActions: 0,
          inProgressActions: 0,
          closedActions: 0,
        },
      });
    }

    const actions = await HazopAction.findAll({
      where: {
        HazopStudyId: { [Op.in]: studyIds },
      },
    });

    // Calculate statistics
    const totalStudies = studies.length;
    const hazopStudies = studies.filter(
      (s) => s.data.studyType === "hazop"
    ).length;
    const hazidStudies = studies.filter(
      (s) => s.data.studyType === "hazid"
    ).length;

    // Count overdue actions (dueDate < today and status != "closed")
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const overdueActions = actions.filter((action) => {
      const dueDate = new Date(action.dueDate);
      return dueDate < today && action.status !== "closed";
    }).length;

    // Count actions by status
    const openActions = actions.filter((a) => a.status === "open").length;
    const inProgressActions = actions.filter(
      (a) => a.status === "in-progress"
    ).length;
    const closedActions = actions.filter((a) => a.status === "closed").length;

    res.json({
      success: true,
      statistics: {
        totalStudies,
        hazopStudies,
        hazidStudies,
        overdueActions,
        openActions,
        inProgressActions,
        closedActions,
      },
    });
  } catch (err) {
    console.error("Error fetching dashboard:", err);
    res.status(500).json({
      success: false,
      message: "Failed to fetch dashboard statistics",
      error: err.message,
    });
  }
};

/**
 * AI Service: Generate HAZOP nodes
 * POST /api/hazop/ai/generate-nodes
 */
const generateNodesController = async (req, res) => {
  try {
    const { processDescription, materials, operatingPressure, operatingTemperature, processType } = req.body;

    if (!processDescription || !materials || !processType) {
      return res.status(400).json({
        success: false,
        message: "processDescription, materials, and processType are required",
      });
    }

    const nodes = await generateNodes({
      processDescription,
      materials,
      operatingPressure: operatingPressure || "N/A",
      operatingTemperature: operatingTemperature || "N/A",
      processType,
    });

    res.json({
      success: true,
      nodes,
      message: "Nodes generated successfully",
    });
  } catch (err) {
    console.error("Error generating nodes:", err);
    res.status(500).json({
      success: false,
      message: "Failed to generate nodes",
      error: err.message,
    });
  }
};

/**
 * AI Service: Analyze HAZOP node
 * POST /api/hazop/ai/analyze-node
 */
const analyzeNodeController = async (req, res) => {
  try {
    const { node, processDescription, materials, operatingConditions } = req.body;

    if (!node || !processDescription || !materials) {
      return res.status(400).json({
        success: false,
        message: "node, processDescription, and materials are required",
      });
    }

    const findings = await analyzeNode({
      node,
      processDescription,
      materials,
      operatingConditions: operatingConditions || "N/A",
    });

    res.json({
      success: true,
      findings,
      message: "Node analyzed successfully",
    });
  } catch (err) {
    console.error("Error analyzing node:", err);
    res.status(500).json({
      success: false,
      message: "Failed to analyze node",
      error: err.message,
    });
  }
};

/**
 * AI Service: Generate HAZID hazards
 * POST /api/hazop/ai/generate-hazards
 */
const generateHazardsController = async (req, res) => {
  try {
    const { facilityName, processDescription, materials, processType, projectPhase } = req.body;

    if (!facilityName || !processDescription || !materials || !processType || !projectPhase) {
      return res.status(400).json({
        success: false,
        message: "facilityName, processDescription, materials, processType, and projectPhase are required",
      });
    }

    const hazards = await generateHazards({
      facilityName,
      processDescription,
      materials,
      processType,
      projectPhase,
    });

    res.json({
      success: true,
      hazards,
      message: "Hazards generated successfully",
    });
  } catch (err) {
    console.error("Error generating hazards:", err);
    res.status(500).json({
      success: false,
      message: "Failed to generate hazards",
      error: err.message,
    });
  }
};

/**
 * AI Service: Chat with AI assistant
 * POST /api/hazop/ai/chat
 */
const chatController = async (req, res) => {
  try {
    const { message, studyContext, conversationHistory } = req.body;

    if (!message || !studyContext) {
      return res.status(400).json({
        success: false,
        message: "message and studyContext are required",
      });
    }

    const aiResponse = await chatWithAI({
      message,
      studyContext,
      conversationHistory: conversationHistory || [],
    });

    res.json({
      success: true,
      message: aiResponse,
    });
  } catch (err) {
    console.error("Error in chat:", err);
    res.status(500).json({
      success: false,
      message: "Failed to process chat",
      error: err.message,
    });
  }
};

/**
 * Close an action with evidence
 * PATCH /api/hazop/actions/:id/close
 */
const closeAction = async (req, res) => {
  try {
    const { id } = req.params;
    const { evidence, closureNotes } = req.body;

    if (!evidence || !closureNotes) {
      return res.status(400).json({
        success: false,
        message: "evidence and closureNotes are required",
      });
    }

    const action = await HazopAction.findByPk(id);

    if (!action) {
      return res.status(404).json({
        success: false,
        message: "Action not found",
      });
    }

    // Update action
    action.status = "closed";
    action.closedAt = new Date();
    action.closedById = req.user.id;
    action.data = {
      ...action.data,
      evidence,
      closureNotes,
    };

    await action.save();

    // Update parent study's action counts
    const study = await HazopStudy.findByPk(action.HazopStudyId);
    if (study) {
      const allActions = await HazopAction.findAll({
        where: { HazopStudyId: study.id },
      });

      const openCount = allActions.filter((a) => a.status !== "closed").length;
      const closedCount = allActions.filter((a) => a.status === "closed").length;

      study.data = {
        ...study.data,
        openActions: openCount,
        closedActions: closedCount,
      };

      await study.save();
    }

    // Log action closure activity
    const studyData = study ? study.toJSON() : null;
    const prefix = studyData && studyData.data.studyType === "hazop" ? "HZP" : "HZD";
    const formattedStudyId = studyData ? addFormattedId(prefix, studyData.id) : null;

    await logActivity({
      userId: req.user.id,
      projectId: studyData ? studyData.ProjectId : null,
      action: "complete",
      entityType: "HAZOP Action",
      entityId: action.id,
      description: getActivityDescription("complete", "HAZOP Action", {
        details: `${action.data.findingRef}`,
      }),
      metadata: {
        studyId: action.HazopStudyId,
        formattedStudyId: formattedStudyId,
        findingRef: action.data.findingRef,
        riskLevel: action.data.riskLevel,
        closedAt: action.closedAt,
        evidence: evidence,
      },
      ipAddress: getIpAddress(req),
      userAgent: getUserAgent(req),
    });

    res.json({
      success: true,
      action: action.toJSON(),
      message: "Action closed successfully",
    });
  } catch (err) {
    console.error("Error closing action:", err);
    res.status(500).json({
      success: false,
      message: "Failed to close action",
      error: err.message,
    });
  }
};

/**
 * Delete a HAZOP/HAZID study and all its actions
 * DELETE /api/hazop/studies/:id
 */
const deleteStudy = async (req, res) => {
  try {
    const { id } = req.params;

    // Check if study exists
    const study = await HazopStudy.findByPk(id);

    if (!study) {
      return res.status(404).json({
        success: false,
        message: "Study not found",
      });
    }

    // Delete all associated actions first
    await HazopAction.destroy({
      where: { HazopStudyId: id },
    });

    // Delete the study
    await HazopStudy.destroy({
      where: { id },
    });

    res.json({
      success: true,
      message: "Study deleted successfully",
    });
  } catch (err) {
    console.error("Error deleting study:", err);
    res.status(500).json({
      success: false,
      message: "Failed to delete study",
      error: err.message,
    });
  }
};

module.exports = {
  getStudies,
  getSingleStudy,
  createStudy,
  deleteStudy,
  getDashboard,
  generateNodesController,
  analyzeNodeController,
  generateHazardsController,
  chatController,
  closeAction,
};
