require("dotenv").config();
const User = require("../../models").users;
const Department = require("../../models").departments;
const Level = require("../../models").levels;
const StudyMode = require("../../models").study_modes;
const ActivityLog = require("../../models").activity_log;
const sequelize = require("../../config/connection");
const Session = require("../../models").sessions;
const Profile = require("../../models").profiles;
const Institute = require("../../models").institutes;
const Activity = require("../helpers/activity.helper");
const jwt = require("jsonwebtoken");
const CryptoJS = require("crypto-js");
const Project = require("../../models").projects;
const ProjectSubmission = require("../../models").project_submissions;
const StudentSupervisor = require("../../models").student_supervisors;
const ProjectTopic = require("../../models").project_topics;
const noticeEmail = require("../helpers/noticeEmail");
const Op = require("sequelize").Op;
var key = CryptoJS.enc.Base64.parse("4gt71TxD1e4P3433");
var iv = CryptoJS.enc.Base64.parse("4gt71TxD1e4P3433");
const restMail = require("../helpers/passwordResetMailer");
const ProjectType = require("../../models").project_types;
const DocumentCategory = require("../../models").document_categories;
const ChatMessage = require("../../models").chatbot_message;
const Interractions = require("../../models").interractions;
const axios = require("axios");
const { google } = require("googleapis");
const fs = require("fs");
const path = require("path");
const pdfParse = require("pdf-parse");
const mammoth = require("mammoth");
const redis = require("redis");
var key = CryptoJS.enc.Base64.parse("4gt71TxD1e4P3433");
var iv  = CryptoJS.enc.Base64.parse("4gt71TxD1e4P3433");

// Load Google credentials
const CREDENTIALS_PATH = path.join(__dirname, "./cred.json");
const MAX_RETRIES = 5;
const BASE_DELAY_MS = 1000;
const GEMINI_MODEL = "gemini-2.5-flash-preview-05-20";
// const GEMINI_MODEL = "gemini-2.5-flash";
const ANALYSIS_RESPONSE_SCHEMA = {
  type: "OBJECT",
  properties: {
    grammar: {
      type: "OBJECT",
      description: "Analysis of grammar and spelling issues.",
      properties: {
        compliance_percentage: {
          type: "INTEGER",
          description: "Estimated grammar and spelling compliance score out of 100.",
        },
        issues: {
          type: "ARRAY",
          items: {
            type: "OBJECT",
            properties: {
              phrase: { type: "STRING", description: "The exact problematic phrase from the document." },
              note: { type: "STRING", description: "The specific error type or suggestion." }
            },
            required: ["phrase", "note"]
          }
        },
        summary: { type: "STRING", description: "A brief summary of grammar and spelling findings." }
      },
      required: ["compliance_percentage", "issues", "summary"]
    },
    relevance_notes: { type: "STRING", description: "Detailed analysis of project relevance to the topic." },
    academic_compliance: { type: "STRING", description: "Analysis of compliance with the academic template/structure." },
    citation: {
      type: "OBJECT",
      description: "Analysis of citation and referencing requirements.",
      properties: {
        phrases_needing_citation: {
          type: "ARRAY",
          items: {
            type: "OBJECT",
            properties: {
              phrase: { type: "STRING", description: "An exact phrase/sentence from the document that requires an inline citation (non-common knowledge)." },
              note: { type: "STRING", description: "The reason why this phrase requires a citation." }
            },
            required: ["phrase", "note"]
          }
        },
        summary: { type: "STRING", description: "Summary of overall citation and referencing issues (e.g., style inconsistency, missing bibliography)." }
      },
      required: ["phrases_needing_citation", "summary"]
    },
    conceptual_alert: { type: "STRING", description: "Conceptual analysis covering the framework, depth of argument, and any plagiarism suspicion." },
    final_summary: { type: "STRING", description: "A short, overall summary (one or two sentences) of the project's quality." }
  },
  required: ["grammar", "relevance_notes", "academic_compliance", "citation", "conceptual_alert", "final_summary"]
};
const CHAT_RESPONSE_SCHEMA = {
  type: "OBJECT",
  properties: {
    answer: { 
      type: "STRING", 
      description: "The concise, accurate answer to the student's question, written in a helpful and advisory tone." 
    },
    // grounding_quote: { 
    //   type: "STRING", 
    //   description: "The exact sentence or short phrase from the provided context that directly supports the answer." 
    // },
    // grounding_context: { 
    //   type: "STRING", 
    //   description: "The full paragraph or surrounding text where the grounding_quote was found, providing broader context." 
    // }
  },
  required: ["answer"]
};

function authorize() {
  const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH, "utf-8"));

  return new google.auth.GoogleAuth({
    credentials,
    scopes: ["https://www.googleapis.com/auth/drive.readonly"],
  });
}

async function getDocContent(fileId) {
  const cacheKey = `doc_content:${fileId}`;

  // ✅ Step 1: Try cache first
  const cachedData = await getCache(cacheKey);
  if (cachedData) {
    console.log(`📦 Cache hit for fileId: ${fileId}`);
    return cachedData;
  }

  console.log(`🚀 Cache miss. Fetching file ${fileId} from Google Drive...`);
  const auth = authorize();
  const drive = google.drive({ version: "v3", auth });

  // Step 2: Get file metadata
  const fileMeta = await drive.files.get({
    fileId,
    fields: "mimeType, name",
  });

  const mimeType = fileMeta.data.mimeType;
  let textContent = "";

  try {
    if (mimeType.startsWith("application/vnd.google-apps.")) {
      // Case 1: Google Docs Editors file → export as plain text
      const doc = await drive.files.export({
        fileId,
        mimeType: "text/plain",
      });
      textContent = doc.data;
    } else {
      // Case 2: Uploaded file (Word/PDF/etc.)
      const file = await drive.files.get(
        { fileId, alt: "media" },
        { responseType: "arraybuffer" }
      );

      const buffer = Buffer.from(file.data);

      if (mimeType === "application/pdf") {
        const pdfData = await pdfParse(buffer);
        textContent = pdfData.text;
      } else if (
        mimeType ===
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
      ) {
        const result = await mammoth.extractRawText({ buffer });
        textContent = result.value;
      } else if (mimeType === "text/plain") {
        textContent = buffer.toString("utf-8");
      } else {
        throw new Error(`Unsupported file type: ${mimeType}`);
      }
    }

    const cleanText = textContent.trim();

    // ✅ Step 3: Cache the result
    await setCache(cacheKey, cleanText, 36000); // cache for 1 hour
    console.log(`💾 Cached file content for fileId: ${fileId}`);

    return cleanText;
  } catch (err) {
    console.error("❌ Error processing file:", err);
    throw err;
  }
}

const redisClient = redis.createClient({
  url: process.env.REDIS_URL,
});
redisClient.connect();

// Helper: Cache functions
const setCache = async (key, value, ttl = 3600) => {
  try {
    await redisClient.setEx(key, ttl, JSON.stringify(value));
  } catch (err) {
    console.error("Redis SET error:", err);
  }
};

const getCache = async (key) => {
  try {
    const data = await redisClient.get(key);
    return data ? JSON.parse(data) : null;
  } catch (err) {
    console.error("Redis GET error:", err);
    return null;
  }
};

const GEMINI_API_KEY = process.env.GEMINI_API_KEY;

async function callGemini(userQuery, systemInstruction) {
  const apiKey = typeof GEMINI_API_KEY !== 'undefined' ? GEMINI_API_KEY : '';
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${apiKey}`;

  const payload = {
    contents: [{ role: "user", parts: [{ text: userQuery }] }],
    systemInstruction: { parts: [{ text: systemInstruction }] },
    generationConfig: {
      responseMimeType: "application/json",
      responseSchema: ANALYSIS_RESPONSE_SCHEMA,
    },
  };

  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
      const response = await axios.post(apiUrl, payload, {
        headers: { "Content-Type": "application/json" },
      });

      const jsonText = response.data.candidates?.[0]?.content?.parts?.[0]?.text;
      if (jsonText) {
        return JSON.parse(jsonText);
      }
      throw new Error("Gemini response was empty or did not contain structured text.");

    } catch (error) {
      if (attempt === MAX_RETRIES - 1) {
        console.error("Gemini API failed after all retries.");
        throw new Error(error.response?.data?.error?.message || `Failed to call Gemini API: ${error.message}`);
      }

      // Exponential backoff
      const delay = BASE_DELAY_MS * 2 ** attempt;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

function findPhraseRanges(content, phrase) {
  const ranges = [];
  let startIndex = 0;
  while ((startIndex = content.indexOf(phrase, startIndex)) !== -1) {
    ranges.push({
      start: startIndex,
      end: startIndex + phrase.length
    });
    startIndex += phrase.length;
  }
  return ranges;
}

async function callGeminiStructured(userQuery, systemInstruction) {
  const apiKey = typeof GEMINI_API_KEY !== 'undefined' ? GEMINI_API_KEY : '';
  const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${apiKey}`;

  const payload = {
    contents: [{ role: "user", parts: [{ text: userQuery }] }],
    systemInstruction: { parts: [{ text: systemInstruction }] },
    generationConfig: {
      responseMimeType: "application/json",
      responseSchema: CHAT_RESPONSE_SCHEMA,
    },
  };

  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
    try {
      const response = await axios.post(apiUrl, payload, {
        headers: { "Content-Type": "application/json" },
      });

      const jsonText = response.data.candidates?.[0]?.content?.parts?.[0]?.text;
      if (jsonText) {
        // --- FIX START: Robust JSON extraction ---
        // 1. Remove common surrounding markdown artifacts (like ```json or ```)
        let cleanedJson = jsonText.trim().replace(/^```(?:json)?\s*|```\s*$/gs, '');
        
        // 2. Locate the first '{' and the last '}' to strip any external commentary/junk
        const startIndex = cleanedJson.indexOf('{');
        const endIndex = cleanedJson.lastIndexOf('}');
        
        if (startIndex === -1 || endIndex === -1) {
             throw new Error("Could not find valid JSON delimiters {} in the model response.");
        }
        
        // Extract only the content between the outermost braces, inclusive
        cleanedJson = cleanedJson.substring(startIndex, endIndex + 1);
        
        // Attempt to parse the cleaned string
        return JSON.parse(cleanedJson);
        // --- FIX END ---
      }
      throw new Error("Gemini response was empty or did not contain structured text.");


    } catch (error) {
      if (attempt === MAX_RETRIES - 1) {
        console.error("Gemini API failed after all retries.");
        throw new Error(error.response?.data?.error?.message || `Failed to call Gemini API: ${error.message}`);
      }

      // Exponential backoff
      const delay = BASE_DELAY_MS * 2 ** attempt;
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}
// helper: apply annotations in Google Docs
async function annotateGoogleDoc(auth, docId, annotations) {
  const docs = google.docs({ version: "v1", auth });
  const requests = [];

  annotations.forEach((ann) => {
    requests.push({
      createComment: {
        content: `[CoVisor] ${ann.note}`,
        location: {
          segmentId: "",
          range: { startIndex: ann.start, endIndex: ann.end }
        }
      }
    });

    requests.push({
      updateTextStyle: {
        range: { startIndex: ann.start, endIndex: ann.end },
        textStyle: {
          backgroundColor: { color: { rgbColor: { red: 1, green: 1, blue: 0 } } } // yellow highlight
        },
        fields: "backgroundColor"
      }
    });
  });

  await docs.documents.batchUpdate({
    documentId: docId,
    requestBody: { requests }
  });

  return { success: true, message: "✅ CoVisor annotations applied to Google Doc" };
}

// =========================================== CoVisor ==========================================
exports.analyzeStudentProjectGemini = async (req, res) => {
  try {
    const { docId } = req.params;
    if (!docId) {
      return res.status(400).json({ error: "Document ID is required." });
    }
    const project = await ProjectSubmission.findOne({ where: { slug: docId }, raw: true });
    if (!project) {
      return res.status(400).json({ error: "Project not found for the given Document ID." });
    }
    const topic = await ProjectTopic.findOne({ where: { id: project.project_id }, raw: true });

    // 🔹 1. Check cache
    const cacheKey = `project_analysis:${docId}`;
    const cachedResult = await getCache(cacheKey);
    if (cachedResult) {
      console.log("✅ Returning cached result for", docId);
      return res.json({ ...cachedResult, source: "cache" });
    }

    // 🔹 2. Extract document text
    const content = await getDocContent(docId);
    if (!content) {
      return res.status(400).json({ error: "Document is empty or could not be read." });
    }

    // 🔹 3. Define Prompts (System instruction and user query)
    const systemInstruction = `
      You are **CoVisor**, a world-class academic supervisor assistant. Your primary task is to provide a comprehensive and structured analysis of the student project based on the user's prompt. You MUST adhere to the JSON schema provided in the generationConfig.
    `;

    const userQuery = `
      Analyze the following student project submission.
      
      **Project Topic:** ${topic.topic || "N/A"}
      
      **Document Content for Analysis:**
      ---
      ${content}
      ---
      
      Your analysis must strictly follow the required JSON structure.
      - **Grammar/Spelling:** Estimate compliance score and list *all* specific problematic phrases and notes.
      - **Relevance:** Detail how closely the content adheres to and develops the assigned topic.
      - **Compliance:** Note any missing or incorrectly formatted academic sections (e.g., abstract, methodology, conclusion).
      - **Citation:** Identify specific claims, data, or arguments that are not common knowledge and require an inline citation.
      - **Conceptual Alert:** Provide insights into the conceptual framework's strength, and flag any suspicions of plagiarism (e.g., sudden style shifts, lack of student voice).
    `;

    // 🔹 4. Gemini Analysis & Structured Parsing
    const analysisJson = await callGemini(userQuery, systemInstruction);

    // 🔹 5. Structure the result from the parsed JSON
    const result = {
      docId,
      percentageCompliance: analysisJson.grammar.compliance_percentage,
      grammarIssues: analysisJson.grammar.summary,
      relevanceNotes: analysisJson.relevance_notes,
      complianceNotes: analysisJson.academic_compliance,
      citationIssues: analysisJson.citation.summary,
      conceptualAlert: analysisJson.conceptual_alert,
      finalSummary: analysisJson.final_summary,
      source: "gemini-api"
    };

    // 🔹 6. Build annotations list for Google Docs
    const annotations = [];

    // Extract phrases directly from the structured JSON arrays
    analysisJson.grammar.issues.forEach((issue) => {
      findPhraseRanges(content, issue.phrase).forEach((range) =>
        annotations.push({ phrase: issue.phrase, note: `Grammar issue: ${issue.note}`, ...range })
      );
    });

    analysisJson.citation.phrases_needing_citation.forEach((issue) => {
      findPhraseRanges(content, issue.phrase).forEach((range) =>
        annotations.push({ phrase: issue.phrase, note: `Citation needed: ${issue.note}`, ...range })
      );
    });

    // 🔹 7. Apply highlights & comments in Google Docs
    const auth = req.authClient;
    if (auth) {
      await annotateGoogleDoc(auth, docId, annotations);
    }
    
    await Activity("CoVisor Report Ready", `Analysis report for ${topic.topic} is now available`, topic.lecturer_user_id);
    // 🔹 8. Cache & Return
    await setCache(cacheKey, result, 36000);
    return res.json(result);

  } catch (error) {
    console.error("Error analyzing project:", error);
    return res.status(500).json({ error: error.message });
  }
};

exports.chatWithFullContext = async (req, res) => {
  try {
    const { docId } = req.params;
    // Assuming req.body.userMessage is an object { message: "..." }
    const { message: userMessage } = req.body.userMessage;

    if (!docId || !userMessage) {
      return res.status(400).json({ error: "Document ID and userMessage are required." });
    }

    // Cache key remains the same
    const cacheKey = `chat:${docId}:${Buffer.from(userMessage).toString("base64")}`;

    // 🧠 Try to fetch cached response
    const cached = await redisClient.get(cacheKey);
    if (cached) {
      console.log("✅ Cache hit for:", cacheKey);
      const cachedData = JSON.parse(cached);

      // Save user message (since a new request came in)
      await ChatMessage.create({
        doc_id: docId,
        sender_type: "user",
        message: userMessage,
      });

      // Save assistant message from cache (retrieving the answer part)
      await ChatMessage.create({
        doc_id: docId,
        sender_type: "assistant",
        message: cachedData.response.answer || "N/A", 
      });

      return res.json({
        response: cachedData.response,
        context_mode: cachedData.context_mode,
        context_used: cachedData.context_used,
        cached: true,
      });
    }

    console.log("⚡ Cache miss for:", cacheKey);
    const content = await getDocContent(docId);
    if (!content) {
      return res.status(400).json({ error: "Document is empty or could not be read." });
    }

    // Save user message
    await ChatMessage.create({
      doc_id: docId,
      sender_type: "user",
      message: userMessage,
    });

    const systemInstruction = `
      You are **CoVisor Chat**, an academic supervisor assistant. You are engaging in a conversation with a student about their project document.
      Use the entire provided document as your context source. Answer the student's question concisely and accurately, always referring back to the document for grounding.
      You MUST return a structured JSON response according to the CHAT_RESPONSE_SCHEMA.
    `;

    const userQuery = `
      **Project Document (FULL CONTEXT):**
      ---
      ${content}
      ---
      
      **Student's Question:** ${userMessage}
    `;

    // 🧩 Call structured Gemini API
    const geminiResponseStructured = await callGeminiStructured(userQuery, systemInstruction);

    // Save assistant message (only save the plain 'answer' text)
    await ChatMessage.create({
      doc_id: docId,
      sender_type: "assistant",
      message: geminiResponseStructured.answer,
    });
    // console.log(geminiResponseStructured)
    const responsePayload = {
      response: geminiResponseStructured.answer, // Returns the structured JSON object
      context_mode: "full_document_structured", // Updated mode for clarity
      context_used: `${content.length} characters (full document)`,
    };

    // 💾 Cache for 1 hour
    await redisClient.setEx(cacheKey, 3600, JSON.stringify(responsePayload));

    return res.json(responsePayload);
  } catch (error) {
    console.error("❌ Error during full-context chat:", error);
    return res.status(500).json({ error: error.message });
  }
};
exports.getChatHistoryByDocID = async (req, res) => {
  try {
    const { docId } = req.params;
    
    if (!docId) {
      return res.status(400).json({ error: "Document ID is required." });
    }
    const messages = await ChatMessage.findAll({
      where:{doc_id:docId},
      order:[["createdAt", "ASC"]],
      raw:true
    })
    return res.json({
      messages,
    });
  } catch (error) {
    console.error("Error during extracted-context chat:", error);
    return res.status(500).json({ error: error.message });
  }
}
// =========================================== AUTH ==========================================

exports.Login = async (req, res) => {
  try {
    const { username, password } = req.body;

    if (!username) {
      return res.status(409).send({
        statusCode: 409,
        success: false,
        message: "Error",
        error: "Username is required",
      });
    }

    if (!password) {
      return res.status(409).send({
        statusCode: 409,
        success: false,
        message: "Error",
        error: "Password is required",
      });
    }

    const user = await User.findOne({
      where: {
        username: username,
      },
      raw: true,
    });

    if (!user) {
      return res.status(404).send({
        statusCode: 404,
        success: false,
        message: "Error",
        error: "An account doesn't exist with this username",
      });
    }

    // Decrypt password
    var decrypted = CryptoJS.AES.decrypt(user.password, key, {
      iv: iv,
    }).toString(CryptoJS.enc.Utf8);
    const match = decrypted === password;

    if (!match) {
      return res.status(409).send({
        statusCode: 409,
        success: false,
        message: "Error",
        error: "Invalid Credentials",
      });
    }
    // Determine account type based on role
    const accountType = user.role == 6 ? 'staff' : 'student';
    const secondRole = user.second_role;
    // Check if user is first time login (you might want to add a field like 'is_first_login' in your User model)
    const isFirstTimeLogin = !user.first_login === true || !user.first_login === 1;

    const token = jwt.sign(
      {
        username,
        userId: user.id,
        role: user.role,
        id: user.id,
        accountType: accountType
      },
      process.env.JWT_pa // Make sure to use the correct environment variable name
    );

  const departments = await Department.findAll({ raw: true, attributes: ['id', 'name'] });

  await Activity("login", "User login", username);
  const profile = await Profile.findOne({ where: { user_id: user.id }, raw: true });
  return res.status(200).send({
    statusCode: 200,
    success: true,
    message: "Login successful",
    token: token,
    departments,
    user: {
      id: user.id,
      username: user.username,
      email: user.email,
      role: user.role,
      accountType: accountType,
      secondRole: secondRole,
      isFirstTimeLogin: isFirstTimeLogin,
      // Include other necessary user fields
      ...profile,
      ...user,
    },
    error: undefined,
  });
    
  } catch (e) {
    console.log(e);
    res.status(500).send({
      statusCode: 500,
      success: false,
      message: "Error",
      error: e.message,
    });
  }
};
exports.getStarted = async (req, res) => {
  try {
    const { email, phone, username, department_id, supervisor_id, password } = req.body;
    // console.log(req.body);
    // Validation helper
    const requiredFields = { email, phone, password };
    for (const [field, value] of Object.entries(requiredFields)) {
      if (!value) {
        return res.status(409).json({
          statusCode: "409",
          success: false,
          message: "Error",
          error: `${field} is required`,
        });
      }
    }

    if (!req.file) {
      return res.status(400).json({
        statusCode: "400",
        success: false,
        message: "Error",
        error: "Please upload a valid file!",
      });
    }

    // Encrypt password if provided
    const hashedPassword = password
      ? CryptoJS.AES.encrypt(password, key, { iv }).toString()
      : null;

    // Update user
    const updateUserData = {
      email: email.toLowerCase().trim(),
      phone,
      first_login: true,
      ...(hashedPassword && { password: hashedPassword }),
    };

    await User.update(updateUserData, { where: { id:req.user.id } });
    const mainUser = await User.findOne({ where: { id:req.user.id  } });

    if (!mainUser) {
      return res.status(409).json({
        statusCode: "409",
        success: false,
        message: "Error",
        error: "Invalid Matric Number",
      });
    }

    // Update profile
    await Profile.update(
      { photo_path: req.file.filename, ...(department_id && { department_id }) },
      { where: { user_id: mainUser.id } }
    );

    // Supervisor handling
    if (supervisor_id) {
      const existingSupervisor = await StudentSupervisor.findOne({
        where: { student_user_id: mainUser.id },
        raw: true,
      });

      if (existingSupervisor) {
        await StudentSupervisor.update(
          { lecturer_user_id: supervisor_id },
          { where: { student_user_id: mainUser.id } }
        );
      } else {
        await StudentSupervisor.create({
          lecturer_user_id: supervisor_id,
          student_user_id: mainUser.id,
          project_type_id: 1,
          session_id: mainUser.session_id,
        });
      }
    }

    return res.status(200).json({
      statusCode: "200",
      success: true,
      message: "Your information has been updated",
    });

  } catch (e) {
    console.error("getStarted error:", e);
    return res.status(500).json({
      statusCode: "500",
      success: false,
      message: "Error",
      error: e.message,
    });
  }
};
exports.sendPassword = async (req, res) => {
try {
  var {
    username
  } = req.body
 
  if (!username) {
    return res.status(409).send({
      statusCode: "409",
      success: false,
      message: 'Error',
      error: 'Username is required'
  })
  }

  let user = await User.findOne({
      where: {
        username
      }
    })
    user = JSON.parse(JSON.stringify(user))
    if(!user){
      return res.status(409).send({
        statusCode: "409",
        success: false,
        message: 'Error',
        error: 'Error, Account doesn\'t exist with your email address'
      })
    }
    if (!user.email) {
      return res.status(409).send({
        statusCode: "409",
        success: false,
        message: 'Error',
        error: 'No email found'
    })
    }

var decrypted = (CryptoJS.AES.decrypt(user.password, key, {iv: iv})).toString(CryptoJS.enc.Utf8);
const saveActivity = await Activity("password", "Request to change password with reset code", user.username)
    console.log(decrypted)
   const sendRestEmail = restMail(user.email, user.username, decrypted);
   if(saveActivity && sendRestEmail){
     
     return res.status(200).send({
       statusCode: "200",
       success: true,
       message: "Success",
       message: 'Password reset code successfully sent'
     })
   }


} catch (e){
  console.log(e)
  res.status(500).send({
    statusCode: "500",
    success: false,
    message: "Error",
    error: e.message
})
}
}
exports.getProfile = async (req, res) => {
  try {
    const user = await Profile.findOne({
      where: { user_id: req.user.id },
      attributes: [
        "department_id",
        "firstname",
        "lastname",
        "middlename",
        "photo_path",
        "title",
      ],
      include: [
        {
          model: User,
          attributes: ["username", "email", "phone", "slug", "role"],
        },
        {
          model: Department,
          attributes: ["id", "name"],
          include: [
            {
              model: Institute,
              attributes: ["name"],
            },
          ],
        },
      ],
    });


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

    return res.status(200).json({
      success: true,
      message: "Profile retrieved successfully",
      data: {
        profile: user,
      },
    });
  } catch (error) {
    console.error("Error fetching profile:", error);
    return res.status(500).json({
      success: false,
      message: "Failed to fetch profile",
      error: error.message,
    });
  }
};



// =========================================== STAFF ==========================================

exports.staffDashboard = async (req, res) => {
  try {
    // Validate user exists
    if (!req.user) {
      return res.status(401).json({
        success: false,
        message: 'Authentication required'
      });
    }
    // console.log(req.user)
    // Redirect if first login
    if (req.user.first_login === false) {
      const redirectUrl = `/user/first-setup?${new URLSearchParams({
        'corass-pro-d': req.user.id,
        'id': req.user.username
      })}`;
      
      return res.status(200).json({
        success: true,
        redirect: redirectUrl,
        message: 'First login setup required'
      });
    }

    // Fetch all required data in parallel
    const [activeSession, stats, recentActivities, submissionChart] = await Promise.all([
      Session.findOne({ where: { status: true }, raw: true }),
      this.getDashboardStats(req.user.id),
      this.getRecentActivities(req.user.username),
      this.getSubmissionChartData(req.user.id)
    ]);

    if (!activeSession) {
      return res.status(404).json({
        success: false,
        message: 'No active academic session found'
      });
    }

    return res.status(200).json({
      success: true,
      data: {
        overview: {
          totalStudents: stats.totalStudents,
          totalTopicProposals: stats.totalTopicProposals,
          totalResearchProjects: stats.totalResearchProjects,
          totalPendingReviews: stats.totalPendingReviews,
          submissionRate: stats.submissionRate
        },
        recentActivities,
        submissionChart,
        activeSession: {
          name: activeSession.name,
          id: activeSession.id
        },
        timestamp: new Date().toISOString()
      }
    });

  } catch (error) {
    console.error('Staff Dashboard Error:', error);
    
    return res.status(500).json({
      success: false,
      message: 'Failed to load dashboard data',
      ...(process.env.NODE_ENV === 'development' && { error: error.message })
    });
  }
};

// Get dashboard statistics
exports.getDashboardStats = async (userId) => {
  const activeSession = await Session.findOne({ where: { status: true }, raw: true });
  
  if (!activeSession) {
    return {
      totalStudents: 0,
      totalTopicProposals: 0,
      totalResearchProjects: 0,
      totalPendingReviews: 0,
      submissionRate: 0
    };
  }

  // Total assigned students (students with approved topics under this lecturer)
  const [totalStudents] = await sequelize.query(
    `SELECT COUNT(DISTINCT pt.student_user_id) as count
     FROM project_topics pt
     WHERE pt.lecturer_user_id = ? 
     AND pt.session_id = ?
     AND pt.is_approved = true
     AND pt.active = true`,
    { replacements: [userId, activeSession.id], type: sequelize.QueryTypes.SELECT }
  );

  // Total topic proposals (all topics assigned to this lecturer)
  const [totalTopicProposals] = await sequelize.query(
    `SELECT COUNT(*) as count
     FROM project_topics
     WHERE lecturer_user_id = ? 
     AND session_id = ?
     AND active = true`,
    { replacements: [userId, activeSession.id], type: sequelize.QueryTypes.SELECT }
  );

  // Total research projects (topics that have submissions)
  const [totalResearchProjects] = await sequelize.query(
    `SELECT COUNT(DISTINCT pt.id) as count
     FROM project_topics pt
     INNER JOIN project_submissions ps ON pt.id = ps.project_id
     WHERE pt.lecturer_user_id = ? 
     AND pt.session_id = ?
     AND ps.active = true`,
    { replacements: [userId, activeSession.id], type: sequelize.QueryTypes.SELECT }
  );

  // Total pending reviews (submissions that need review)
  const [totalPendingReviews] = await sequelize.query(
    `SELECT COUNT(*) as count
     FROM project_submissions ps
     INNER JOIN project_topics pt ON ps.project_id = pt.id
     WHERE pt.lecturer_user_id = ? 
     AND ps.session_id = ?
     AND ps.is_approved = '0'
     AND ps.active = true`,
    { replacements: [userId, activeSession.id], type: sequelize.QueryTypes.SELECT }
  );

  // Submission rate (percentage of students who have submitted at least one document)
  const [submissionStats] = await sequelize.query(
    `SELECT 
       COUNT(DISTINCT pt.student_user_id) as total_students,
       COUNT(DISTINCT CASE WHEN ps.id IS NOT NULL THEN pt.student_user_id END) as submitted_students
     FROM project_topics pt
     LEFT JOIN project_submissions ps ON pt.id = ps.project_id AND ps.active = true
     WHERE pt.lecturer_user_id = ? 
     AND pt.session_id = ?
     AND pt.is_approved = true
     AND pt.active = true`,
    { replacements: [userId, activeSession.id], type: sequelize.QueryTypes.SELECT }
  );

  const submissionRate = submissionStats.total_students > 0 
    ? Math.round((submissionStats.submitted_students / submissionStats.total_students) * 100)
    : 0;

  return {
    totalStudents: parseInt(totalStudents?.count) || 0,
    totalTopicProposals: parseInt(totalTopicProposals?.count) || 0,
    totalResearchProjects: parseInt(totalResearchProjects?.count) || 0,
    totalPendingReviews: parseInt(totalPendingReviews?.count) || 0,
    submissionRate
  };
};

// Get recent activities
// Get recent activities from activity_log table
exports.getRecentActivities = async (userId) => {
  try {
    const activeSession = await Session.findOne({ where: { status: true }, raw: true });
    
    if (!activeSession) return [];

    const activities = await ActivityLog.findAll({
      where: { causer_id: userId },
      limit: 5,
      order: [["createdAt", "DESC"]],
      attributes: ["createdAt", "description"],
      raw: true
    });
    // Handle both array and single object responses
    let activitiesArray = [];
    if (Array.isArray(activities)) {
      activitiesArray = activities;
    } else if (activities && typeof activities === 'object') {
      activitiesArray = [activities];
    }

    // console.log('Raw Activities from DB:', activitiesArray); // Debug log

    // Process activities based on event type and description
    const processedActivities = activitiesArray.filter(activity => activity).map(activity => {
      let action = '';
      let user = 'System';
      
      // Determine user name
      if (activity.firstname && activity.lastname) {
        user = `${activity.firstname} ${activity.lastname}`;
      } else if (activity.username) {
        user = activity.username;
      }

      // Parse properties if it's a JSON string
      let properties = {};
      if (activity.properties) {
        try {
          properties = typeof activity.properties === 'string' 
            ? JSON.parse(activity.properties) 
            : activity.properties;
        } catch (e) {
          console.error('Error parsing properties:', e);
        }
      }

      // Determine action based on event and description
      switch (activity.event) {
        case 'submission_created':
          action = `submitted ${properties.document_type || 'a document'}`;
          break;
        case 'topic_approved':
          action = 'approved a topic';
          break;
        case 'topic_rejected':
          action = 'rejected a topic';
          break;
        case 'message_sent':
          action = 'sent you a message';
          break;
        case 'review_completed':
          action = 'completed a review';
          break;
        default:
          // Fallback to description parsing
          if (activity.description) {
            if (activity.description.includes('submitted')) {
              action = activity.description;
            } else if (activity.description.includes('approved')) {
              action = activity.description;
            } else if (activity.description.includes('reviewed')) {
              action = activity.description;
            } else {
              action = activity.description;
            }
          } else {
            action = 'performed an action';
          }
      }

      return {
        id: activity.id,
        type: activity.event || 'activity',
        user: user,
        action: action,
        time: this.formatTimeAgo(activity.createdAt),
        rawDescription: activity.description,
        properties: properties
      };
    });

    // console.log('Processed Activities:', processedActivities); // Debug log
    return processedActivities;

  } catch (error) {
    console.error('Error in getRecentActivities:', error);
    return [];
  }
};


// Get submission chart data (last 6 months)
// Get submission chart data (last 6 months)
exports.getSubmissionChartData = async (userId) => {
  try {
    const sixMonthsAgo = new Date();
    sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);

    const chartData = await sequelize.query(
      `SELECT 
         DATE_FORMAT(ps.createdAt, '%b') as month,
         COUNT(ps.id) as count
       FROM project_submissions ps
       INNER JOIN project_topics pt ON ps.project_id = pt.id
       WHERE pt.lecturer_user_id = ?
       AND ps.createdAt >= ?
       AND ps.active = true
       GROUP BY DATE_FORMAT(ps.createdAt, '%Y-%m'), DATE_FORMAT(ps.createdAt, '%b')
       ORDER BY DATE_FORMAT(ps.createdAt, '%Y-%m') ASC
       LIMIT 6`,
      { 
        replacements: [userId, sixMonthsAgo],
        type: sequelize.QueryTypes.SELECT 
      }
    );

    // console.log('Chart Data Result:', chartData); // Debug log

    // Handle both array and single object responses
    let dataArray = [];
    if (Array.isArray(chartData)) {
      dataArray = chartData;
    } else if (chartData && typeof chartData === 'object') {
      // If it's a single object, wrap it in an array
      dataArray = [chartData];
    }

    // console.log('Processed Chart Data Array:', dataArray); // Debug log

    // Get last 6 months
    const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    const currentMonthIndex = new Date().getMonth();
    
    // Get last 6 months including current month
    const last6Months = [];
    for (let i = 5; i >= 0; i--) {
      const monthIndex = (currentMonthIndex - i + 12) % 12;
      last6Months.push(months[monthIndex]);
    }

    // console.log('Last 6 Months:', last6Months); // Debug log

    // Map chart data to months
    const result = last6Months.map(month => {
      const monthData = dataArray.find(data => data && data.month === month);
      return {
        month,
        count: monthData ? parseInt(monthData.count) : 0
      };
    });

    // console.log('Final Chart Data:', result); // Debug log
    return result;

  } catch (error) {
    console.error('Error in getSubmissionChartData:', error);
    
    // Return default data on error
    const months = ['Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov'];
    return months.map(month => ({ month, count: 0 }));
  }
};

// Helper function to format time ago
exports.formatTimeAgo = (date) => {
  const now = new Date();
  const past = new Date(date);
  const diffInSeconds = Math.floor((now - past) / 1000);

  if (diffInSeconds < 60) return 'Just now';
  if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} mins ago`;
  if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`;
  return `${Math.floor(diffInSeconds / 86400)} days ago`;
};

//notifications
exports.getNotificationsHeader = async (req, res) => {
  try {
    const notifications = await ActivityLog.findAll({
      where: { causer_id: req.user.id },
      order: [["createdAt", "DESC"]],
      limit: 5,
      raw: true,
    });
    //count unread notifications
    const unreadCount = await ActivityLog.count({
      where: { causer_id: req.user.id, event: false }
    });

    // Mark all as read
    await ActivityLog.update({ event: true }, { where: { causer_id: req.user.id, event: false } });
    return res.status(200).json({
      success: true,
      data: notifications,
      unreadCount
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: 'Failed to load notifications',
      ...(process.env.NODE_ENV === 'development' && { error: error.message })
    });
  }
}
exports.getAllNotifications = async (req, res) => {
  try {
    const { filter = 'all', page = 1, limit = 10 } = req.query;
    const offset = (page - 1) * limit;
    
    let whereClause = { causer_id: req.user.id };
    
    // Apply filters based on tab selection
    if (filter === 'unread') {
      whereClause.event = false;
    } else if (filter === 'read') {
      whereClause.event = true;
    }
    // 'all' shows all notifications
    
    const { count, rows: notifications } = await ActivityLog.findAndCountAll({
      where: whereClause,
      order: [["createdAt", "DESC"]],
      limit: parseInt(limit),
      offset: offset,
      raw: true,
    });
    
    const unreadCount = await ActivityLog.count({
      where: { causer_id: req.user.id, event: false }
    });

    return res.status(200).json({
      success: true,
      data: notifications,
      pagination: {
        currentPage: parseInt(page),
        totalPages: Math.ceil(count / limit),
        totalItems: count,
        itemsPerPage: parseInt(limit)
      },
      unreadCount,
      currentFilter: filter
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: 'Failed to load notifications',
      ...(process.env.NODE_ENV === 'development' && { error: error.message })
    });
  }
}
exports.markAllAsRead = async (req, res) => {
  try {
    await ActivityLog.update(
      { event: true }, 
      { 
        where: { 
          causer_id: req.user.id, 
          event: false 
        } 
      }
    );

    
    //count unread notifications
    const unreadCount = await ActivityLog.count({
      where: { causer_id: req.user.id, event: false }
    });


    return res.status(200).json({
      success: true,
      message: 'All notifications marked as read',
      unreadCount
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: 'Failed to mark notifications as read',
      ...(process.env.NODE_ENV === 'development' && { error: error.message })
    });
  }
}
exports.markAsRead = async (req, res) => {
  try {
    const { id } = req.params;
    
    const notification = await ActivityLog.findOne({
      where: { 
        id: id,
        causer_id: req.user.id 
      }
    });

    if (!notification) {
      return res.status(404).json({
        success: false,
        message: 'Notification not found'
      });
    }

    await ActivityLog.update(
      { event: true }, 
      { 
        where: { 
          id: id,
          causer_id: req.user.id 
        } 
      }
    );

    const unreadCount = await ActivityLog.count({
      where: { causer_id: req.user.id, event: false }
    });

    return res.status(200).json({
      success: true,
      message: 'Notification marked as read',
      unreadCount
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: 'Failed to mark notification as read',
      ...(process.env.NODE_ENV === 'development' && { error: error.message })
    });
  }
}
exports.studentManagementAPI = async (req, res) => {
  try {
    const department_id = req.department_id;
    const { search, session: sessionFilter, level: levelFilter, study_mode: studyModeFilter } = req.query;

    // Get active session
    const activeSession = await Session.findOne({ where: { status: true } });
    if (!activeSession) {
      return res.status(404).json({
        success: false,
        message: "No active session found"
      });
    }

    // Base where clause for students
    let studentWhereClause = {
      department_id,
      role_id: 7,
    };

    // Use filtered session or active session
    const targetSessionId = sessionFilter || activeSession.id;
    studentWhereClause.session_id = targetSessionId;

    // Apply additional filters if provided
    if (levelFilter) studentWhereClause.level_id = levelFilter;
    if (studyModeFilter) studentWhereClause.study_mode_id = studyModeFilter;

    // Parallel database queries
    const [
      levels,
      study_modes,
      supervisors,
      departments,
      department,
      allSessions,
      students
    ] = await Promise.all([
      Level.findAll(),
      StudyMode.findAll(),
      Profile.findAll({
        where: { department_id, role_id: 6 },
      }),
      Department.findAll({ where: { active: true } }),
      Department.findByPk(department_id),
      Session.findAll({ order: [["createdAt", "DESC"]] }),
      Profile.findAll({
        where: studentWhereClause,
        order: [["createdAt", "DESC"]],
        include: [
          {
            model: User,
            where: search ? {
              [Op.or]: [
                { username: { [Op.like]: `%${search}%` } },
                { email: { [Op.like]: `%${search}%` } },
                { phone: { [Op.like]: `%${search}%` } }
              ]
            } : {},
            attributes: ["username", "email", "phone", "id"],
            required: !!search
          },
          {
            model: StudyMode,
          },
          {
            model: Level,
          },
        ],
      })
    ]);

    // Get dynamic statistics by level
    const levelStats = await Promise.all(
      levels.map(async (level) => {
        const count = await Profile.count({
          where: {
            ...studentWhereClause,
            level_id: level.id
          }
        });
        return {
          levelId: level.id,
          levelName: level.name,
          count: count
        };
      })
    );

    // Get dynamic statistics by study mode
    const studyModeStats = await Promise.all(
      study_modes.map(async (studyMode) => {
        const count = await Profile.count({
          where: {
            ...studentWhereClause,
            study_mode_id: studyMode.id
          }
        });
        return {
          studyModeId: studyMode.id,
          studyModeName: studyMode.name,
          count: count
        };
      })
    );

    // Calculate total active students (sum of all levels or study modes)
    const totalActive = levelStats.reduce((sum, stat) => sum + stat.count, 0);
    const totalInactive = 0; // Update based on your business logic

    // Format students data
    const formattedStudents = students.map(student => ({
      id: student.id,
      studentName: student.User?.username || 'N/A',
      matricNumber: student.matric_number || `U${10000 + student.id}`,
      level: student.Level?.name || 'N/A',
      studyMode: student.StudyMode?.name || 'N/A',
      session: allSessions.find(s => s.id === targetSessionId)?.name || 'N/A',
      contact: {
        email: student.User?.email || 'N/A',
        phone: student.User?.phone || 'N/A'
      },
      actions: student.User?.id
    }));

    const response = {
      success: true,
      data: {
        // Summary section
        summary: {
          totalActive,
          totalInactive,
          totalStudents: totalActive + totalInactive
        },
        
        // Dynamic statistics
        statistics: {
          byLevel: levelStats.reduce((acc, stat) => {
            acc[stat.levelName.toLowerCase()] = stat.count;
            return acc;
          }, {}),
          byStudyMode: studyModeStats.reduce((acc, stat) => {
            // Convert to camelCase or proper naming
            const key = stat.studyModeName.toLowerCase().replace('-', '').replace(' ', '');
            acc[key] = stat.count;
            return acc;
          }, {})
        },

        // Raw statistics for more flexibility
        rawStatistics: {
          levels: levelStats,
          studyModes: studyModeStats
        },
        
        // Filter options
        filters: {
          sessions: allSessions,
          levels: levels,
          studyModes: study_modes
        },
        
        // Students list
        students: {
          list: formattedStudents,
          totalCount: formattedStudents.length
        },
        
        // Department information
        departmentInfo: {
          currentDepartment: department,
          allDepartments: departments,
          supervisors: supervisors
        },
        
        // Applied filters (echo back what was filtered)
        appliedFilters: {
          search: search || '',
          session: sessionFilter || '',
          level: levelFilter || '',
          study_mode: studyModeFilter || '',
          currentSession: {
            id: targetSessionId,
            name: allSessions.find(s => s.id === targetSessionId)?.name
          }
        }
      }
    };

    return res.status(200).json(response);

  } catch (error) {
    console.error("Student Management API Error:", error);
    return res.status(500).json({
      success: false,
      message: "Internal server error",
      error: error.message
    });
  }
};
exports.myAssignStudentAPI = async (req, res) => {
  try {
    let allStudents = [];
    let pendingApproval = 0;
    let totalInactive = 0;
    const activeSession = await Session.findOne({where:{status:true}, order:[["createdAt","DESC"]], raw:true})
    const session_id = req.query.session_id || activeSession.id;
    const { search, level, study_mode } = req.query;

    // Parallel database queries for better performance
    const [levels, study_modes, projectSubmission, students] = await Promise.all([
      Level.findAll(),
      StudyMode.findAll(),
      Project.findAll({
        where: {
          lecturer_user_id: req.user.id,
        },
        include: [{ model: ProjectSubmission }],
      }),
      StudentSupervisor.findAll({
        where: {
          lecturer_user_id: req.user.id,
          session_id
        },
        attributes: ["student_user_id"],
        raw:true
      })
    ]);

    // Calculate pending approvals
    for (const project of JSON.parse(JSON.stringify(projectSubmission))) {
      if (project.project_submissions && project.project_submissions.is_approved === false) {
        pendingApproval++;
      }
    }
    // Process each student
    for (const student of students) {
      let user = await Profile.findOne({
        where: { 
          user_id: student.student_user_id
        },
        attributes: ["firstname", "lastname", "middlename", "user_id"],
        include: [
          {
            model: User,
            attributes: ["username", "email", "phone", "id"],
          },
          {
            model: Level,
          },
          {
            model: StudyMode,
          },
        ],
      });

      let project = await ProjectTopic.findOne({
        where: { 
          student_user_id: student.student_user_id, 
        },
        attributes: ["id", "topic"],
        include: [
          {
            model: ProjectSubmission,
            where: { 
              document_category_id: 7, 
              is_approved: true 
            },
            required: false,
            attributes: ["id", "is_approved", "document_category_id"]
          }
        ]
      });

      // Check if student has no approved project (inactive)
      if (!project || !project.project_submissions || project.project_submissions.length === 0) {
        totalInactive++;
      }

      project = JSON.parse(JSON.stringify(project));

      if (user) {
        user = JSON.parse(JSON.stringify(user));
        // Create student object
        const studentData = {
          id: user.user_id,
          studentName: `${user.firstname || ''} ${user.middlename || ''} ${user.lastname || ''}`.trim(),
          matricNumber: user.user?.username || 'N/A',
          level: user.level?.name || 'N/A',
          studyMode: user.study_mode?.name || 'N/A',
          session: session_id,
          contact: {
            email: user.user?.email || 'N/A',
            phone: user.user?.phone || 'N/A'
          },
          project: project ? {
            projectId: project.id,
            topic: project.topic || 'N/A',
            submission: project.project_submissions && project.project_submissions.length > 0 ? {
              id: project.project_submissions[0].id,
              isApproved: project.project_submissions[0].is_approved,
              documentCategoryId: project.project_submissions[0].document_category_id
            } : null
          } : null,
          status: project && project.project_submissions && project.project_submissions.length > 0 ? 'active' : 'inactive'
        };

        allStudents.push(studentData);
      }
    }

    // Apply search and filters
    let filteredStudents = allStudents;
    
    if (search) {
      filteredStudents = filteredStudents.filter(student => 
        student.studentName.toLowerCase().includes(search.toLowerCase()) ||
        student.matricNumber.toLowerCase().includes(search.toLowerCase()) ||
        student.contact.email.toLowerCase().includes(search.toLowerCase())
      );
    }

    if (level) {
      filteredStudents = filteredStudents.filter(student => 
        student.level.toLowerCase() === level.toLowerCase()
      );
    }

    if (study_mode) {
      filteredStudents = filteredStudents.filter(student => 
        student.studyMode.toLowerCase() === study_mode.toLowerCase()
      );
    }

    // Calculate statistics
    const totalActive = filteredStudents.filter(student => student.status === 'active').length;
    const filteredInactive = filteredStudents.filter(student => student.status === 'inactive').length;
    const sessions = await Session.findAll({ order: [["createdAt", "DESC"]], attributes:["id", "name"], raw: true });
    const response = {
      success: true,
      data: {
        summary: {
          totalStudents: filteredStudents.length,
          totalActive,
          totalInactive: filteredInactive,
          pendingApproval
        },
        statistics: {
          byLevel: filteredStudents.reduce((acc, student) => {
            const level = student.level || 'Unknown';
            acc[level] = (acc[level] || 0) + 1;
            return acc;
          }, {}),
          byStudyMode: filteredStudents.reduce((acc, student) => {
            const studyMode = student.studyMode || 'Unknown';
            acc[studyMode] = (acc[studyMode] || 0) + 1;
            return acc;
          }, {}),
          byStatus: {
            active: totalActive,
            inactive: filteredInactive
          }
        },
        filters: {
          sessions: sessions,
          levels: levels,
          studyModes: study_modes,
          currentSession: session_id
        },
        students: {
          list: filteredStudents,
          totalCount: filteredStudents.length
        },
        appliedFilters: {
          search: search || '',
          level: level || '',
          study_mode: study_mode || '',
          session: session_id || ''
        }
      }
    };

    return res.status(200).json(response);

  } catch (error) {
    console.error("My Assign Student API Error:", error);
    return res.status(500).json({
      success: false,
      message: "Internal server error",
      error: error.message,
      data: {
        summary: {
          totalStudents: 0,
          totalActive: 0,
          totalInactive: 0,
          pendingApproval: 0
        },
        statistics: {
          byLevel: {},
          byStudyMode: {},
          byStatus: { active: 0, inactive: 0 }
        },
        filters: {
          sessions: [],
          levels: [],
          studyModes: [],
          currentSession: null
        },
        students: {
          list: [],
          totalCount: 0
        },
        appliedFilters: {}
      }
    });
  }
};
exports.getTopics = async (req, res) => {
  try {
    const { search, session_id, level_id, study_mode_id, status } = req.query;

    // Get the active session if none is passed
    const activeSession = session_id
      ? await Session.findByPk(session_id)
      : await Session.findOne({ where: { status: true }, raw: true });
    const sessions = await Session.findAll({ order: [["createdAt", "DESC"]] });
    const levels = await Level.findAll();
    const study_modes = await StudyMode.findAll();

    // Base where conditions
    const commonWhereConditions = {
      lecturer_user_id: req.user.id,
      session_id: activeSession?.id,
    };

    // Status filters
    if (status === "Approved") commonWhereConditions.is_approved = true;
    else if (status === "Pending") commonWhereConditions.is_approved = false;
    else if (status === "Rejected") commonWhereConditions.active = false;
    else commonWhereConditions.active = true; // Default

    // Search filter
    let searchCondition = {};
    if (search) {
      searchCondition = {
        [Op.or]: [
          { topic: { [Op.like]: `%${search}%` } },
          { "$student.username$": { [Op.like]: `%${search}%` } },
          { "$student.profile.firstname$": { [Op.like]: `%${search}%` } },
        ],
      };
    }

    // Join filters (Level, Study Mode)
    const includeFilters = [
      {
        model: User,
        as: "student",
        attributes: ["username", ],
        include: [
          {
            model: Profile,
            include: [
              { model: Level },
              { model: StudyMode },
            ],
          },
        ],
      },
      { model: ProjectType },
    ];

    // Fetch topics
    const topics = await ProjectTopic.findAll({
      where: {
        ...commonWhereConditions,
        ...searchCondition,
      },
      include: includeFilters,
      order: [["createdAt", "DESC"]],
    });

    // Filter by level and study mode (after join)
    const filteredTopics = topics.filter((topic) => {
      const topicLevel = topic.student?.Profile?.Level?.id;
      const topicStudyMode = topic.student?.Profile?.StudyMode?.id;
      return (
        (!level_id || topicLevel === parseInt(level_id)) &&
        (!study_mode_id || topicStudyMode === parseInt(study_mode_id))
      );
    });

    // Count summary
    const totalTopics = await ProjectTopic.count({
      where: { lecturer_user_id: req.user.id, session_id: activeSession?.id },
    });

    const totalApproved = await ProjectTopic.count({
      where: { lecturer_user_id: req.user.id, session_id: activeSession?.id, is_approved: true, active: true },
    });

    const totalRejected = await ProjectTopic.count({
      where: { lecturer_user_id: req.user.id, session_id: activeSession?.id, active: false, is_approved: false  },
    });

    const totalPending = await ProjectTopic.count({
      where: { lecturer_user_id: req.user.id, session_id: activeSession?.id, is_approved: false, active:true  },
    });
    // console.log("Filtered Topics:", filteredTopics); // Debug log
    // Response
    return res.status(200).json({
      success: true,
      data: {
        summary: {
          totalTopics,
          totalApproved,
          totalRejected,
          totalPending,
        },
        topics: filteredTopics.map((topic) => ({
          id: topic.id,
          title: topic.topic,
          status: topic.is_approved && topic.active
            ? "Approved"
            : topic.active && !topic.is_approved
            ? "Submitted"
            : "Rejected",
          level: topic.student?.profile?.level?.name || "N/A",
          studyMode: topic.student?.profile?.study_mode?.name || "N/A",
          session: activeSession?.name || "",
          student: {
            name: topic.student?.profile?.firstname + " " + topic.student?.profile?.lastname|| "Unknown",
            matricNo: topic.student?.username || "",
          },
          date: topic.createdAt,
        })),
        filters: {
          sessions,
          levels,
          study_modes,
        },
      },
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      success: false,
      message: error.message,
      data: {
        summary: {
          totalTopics: 0,
          totalApproved: 0,
          totalRejected: 0,
          totalPending: 0,
        },
        topics: [],
      },
    });
  }
};
exports.approveTopic = async (req, res) => {
  try {
    const { id } = req.params;
    const { editedTopic, supervisorComment } = req.body;

    if (!id ) {
      return res.status(400).json({
        success: false,
        message: "Missing required fields: topic id ",
      });
    }
    const topic = await ProjectTopic.findOne({where:{id}, raw:true})
    if(!topic){
      return res.status(404).json({
        success: false,
        message: "Topic not found",
      });
    }
    // Reset other topics for same student + supervisor
    await ProjectTopic.update(
      { is_approved: false, active: false },
      {
        where: {
          student_user_id: topic.student_user_id,
          lecturer_user_id: topic.lecturer_user_id,
          project_type_id:topic.project_type_id,
        },
      }
    );

    // Approve selected topic
    const [rowsUpdated] = await ProjectTopic.update(
      {
        is_approved: true,
        active: true,
        approval_comment: supervisorComment,
        topic: editedTopic || undefined,
      },
      { where: { id } }
    );

    if (!rowsUpdated) {
      return res.status(404).json({
        success: false,
        message: "Topic not found or update failed.",
      });
    }

    // Send notification mail asynchronously
    sendNoticeMail(topic.student_user_id, "topic", "true", id, supervisorComment).catch(
      (err) => console.error("Mail send failed:", err.message)
    );

    return res.status(200).json({
      success: true,
      message: "Topic successfully approved",
    });
  } catch (error) {
    console.error("approveTopic error:", error);
    return res.status(500).json({
      success: false,
      message: error.message,
    });
  }
};
exports.rejectTopic = async (req, res) => {
  try {
    const { id } = req.params;
    const { supervisorComment } = req.body;

    if (!id ) {
      return res.status(400).json({
        success: false,
        message: "Missing required fields: topic id ",
      });
    }
    const topic = await ProjectTopic.findOne({where:{id}, raw:true})
    if(!topic){
      return res.status(404).json({
        success: false,
        message: "Topic not found",
      });
    }
    // Reject topic
    const [rowsUpdated] = await ProjectTopic.update(
      {
        is_approved: false,
        active: false,
        approval_comment: supervisorComment,
      },
      { where: { id } }
    );

    if (!rowsUpdated) {
      return res.status(404).json({
        success: false,
        message: "Topic not found or update failed.",
      });
    }
    // console.log(rowsUpdated)
    // Send notification mail asynchronously
    sendNoticeMail(topic.student_user_id, "topic", "false", id, supervisorComment).catch(
      (err) => console.error("Mail send failed:", err.message)
    );

    return res.status(200).json({
      success: true,
      message: "Topic successfully rejected",
    });
  } catch (error) {
    console.error("rejectTopic error:", error);
    return res.status(500).json({
      success: false,
      message: error.message,
    });
  }
};
exports.getStudentProjects = async (req, res) => {
  try {
    const {
      documentCategory,
      session,
      levelId,
      studyMode,
      search,
      status,
    } = req.query;
    const sessionId = session || null;
    const studyModeId = studyMode || null;
    const [docs, levels, study_modes, activeSession] = await Promise.all([
      DocumentCategory.findAll({ where: { active: true } }),
      Level.findAll(),
      StudyMode.findAll(),
      Session.findOne({ where: { status: true }, raw: true }),
    ]);

    const [[summary]] = await sequelize.query(`
      SELECT
        SUM(CASE WHEN ps.is_approved = '0' AND ps.active = '1' THEN 1 ELSE 0 END) AS projectInprogress,
        SUM(CASE WHEN ps.is_approved = '1' AND ps.active = '1' THEN 1 ELSE 0 END) AS projectInApproved,
        SUM(CASE WHEN ps.active = '0' AND ps.is_approved = '0' THEN 1 ELSE 0 END) AS projectRejected,
        SUM(CASE WHEN ps.is_approved = '1' AND ps.active = '1' THEN 1 ELSE 0 END) AS projectCompleted
      FROM project_submissions ps
      INNER JOIN project_topics pt ON ps.project_id = pt.id
      WHERE pt.lecturer_user_id = '${req.user.id}'
      AND ps.session_id = '${activeSession.id}'
    `);

    const whereClause = {
      lecturer_user_id: req.user.id,
      session_id: sessionId || activeSession.id,
    };

    // ✅ Build submission filter dynamically
    const submissionWhere = {
      session_id: sessionId || activeSession.id,
      ...(documentCategory && { document_category_id: documentCategory }),
    };

    if (status === "Approved") {
      submissionWhere.is_approved = true;
      submissionWhere.active = true;
    } else if (status === "Pending" || status === "Submitted") {
      submissionWhere.is_approved = false;
      submissionWhere.active = true;
    } else if (status === "Rejected") {
      submissionWhere.is_approved = false;
      submissionWhere.active = false;
    }

    const topics = await ProjectTopic.findAll({
      where: whereClause,
      include: [
        {
          model: ProjectSubmission,
          required: true,
          where: submissionWhere, // ✅ Correctly applied here
          include: [
            {
              model: DocumentCategory,
              attributes: ['name'],
            },
            {
              model: ProjectTopic,
              include: [
                {
                  model: User,
                  as: 'student',
                  attributes: ['username'],
                  include: [
                    {
                      model: Profile,
                      attributes: ['firstname', 'lastname', 'middlename'],
                      include: [
                        {
                          model: Level,
                          attributes: ['name', 'id'],
                          ...(levelId && { where: { id: levelId } }),
                        },
                        {
                          model: StudyMode,
                          attributes: ['name', 'id'],
                          ...(studyModeId && { where: { id: studyModeId } }),
                        },
                      ],
                    },
                  ],
                },
              ],
            },
          ],
        },
      ],
    });

    let projects = [];
    for (const topic of topics) {
      for (const submission of topic.project_submissions) {
        const computedStatus = !submission
          ? 'N/A'
          : submission.is_approved && submission.active
          ? 'Approved'
          : !submission.is_approved && submission.active
          ? 'Pending'
          : !submission.is_approved && !submission.active
          ? 'Rejected'
          : 'N/A';

        projects.push({
          ...submission.toJSON(),
          status: computedStatus,
        });
      }
    }

    if (search) {
      const lowerSearch = search.toLowerCase();
      projects = projects.filter((p) => {
        const topicTitle = p.project_topic?.topic?.toLowerCase() || '';
        const studentName = `${p.project_topic?.student?.profile?.firstname || ''} ${p.project_topic?.student?.profile?.lastname || ''} `.toLowerCase();
        return topicTitle.includes(lowerSearch) || studentName.includes(lowerSearch);
      });
    }

    const sessions = await Session.findAll({ order: [['createdAt', 'DESC']], raw: true });
    // console.log(summary)
    return res.status(200).json({
      success: true,
      data: {
        summary: {
          totalProjects: projects.length,
          projectInprogress: summary.projectInprogress || 0,
          projectCompleted: summary.projectCompleted || 0,
          projectRejected: summary.projectRejected || 0,
          projectInApproved: summary.projectInApproved || 0,
        },
        filters: { docs, sessions, levels, study_modes },
        projects,
      },
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      success: false,
      message: error.message,
    });
  }
};
exports.approveDocument = async (req, res) => {
  try {
    const { docId, message, actionType } = req.body;

    if (!docId || !actionType) {
      return res.status(400).json({ success: false, message: "Missing required fields" });
    }

    // Fetch submission once
    const submission = await ProjectSubmission.findOne({
      where: { id: docId },
      attributes: ["id", "project_id", "document_category_id"],
      raw: true,
    });

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

    // Prepare update data based on action
    const isAccepted = actionType === "accept";
    const updateData = {
      is_approved: isAccepted,
      active: isAccepted,
      approval_comment: message || null,
    };

    // Update submission
    await ProjectSubmission.update(updateData, { where: { id: docId } });

    // If accepted and document is final report (category_id == 7), mark project as completed
    if (isAccepted && submission.document_category_id == 7) {
      await Project.update(
        { is_completed: true, active: true },
        { where: { id: submission.project_id } }
      );
    }

    // Send notification (async, non-blocking)
    sendNoticeMailProject(
      isAccepted ? "true" : "false",
      submission,
      message
    );

    return res.status(200).json({
      success: true,
      message: isAccepted
        ? "Document successfully approved"
        : "Document successfully rejected",
    });
  } catch (error) {
    console.error("❌ approveDocument error:", error);
    return res.status(500).json({
      success: false,
      message: "Internal server error",
      error: error.message,
    });
  }
};
exports.getStudentContacts = async (req, res) => {
  try {
    const { search, studentId } = req.query;

    // Fetch all interactions for this user
    const interractions = await Interractions.findAll({
      where: {
        [Op.or]: [
          { send_user_id: req.user.id },
          { receive_user_id: req.user.id },
        ],
      },
      include: [
        { model: User, as: "receiver", include: [{ model: Profile }] },
        { model: User, as: "sender", include: [{ model: Profile }] },
      ],
      order: [["createdAt", "DESC"]],
    });

    // Fetch students supervised by this lecturer
    const students = await StudentSupervisor.findAll({
      where: { lecturer_user_id: req.user.id },
      include: [{ model: User, as: "student", include: [{ model: Profile }] }],
    });

    // Filter by search (name or ID)
    let filteredStudents = students;
    if (search) {
      const searchTerm = search.toLowerCase();
      filteredStudents = filteredStudents.filter((s) => {
        const name = s.student?.Profile?.full_name?.toLowerCase() || "";
        const id = s.student?.id?.toString() || "";
        return name.includes(searchTerm) || id.includes(searchTerm);
      });
    }

    // Filter by specific studentId
    if (studentId) {
      filteredStudents = filteredStudents.filter(
        (s) => s.student?.id?.toString() === studentId.toString()
      );
    }

    return res.json({
      success: true,
      data: {
        interractions,
        students: filteredStudents,
      },
    });
  } catch (error) {
    console.error("Error fetching student contacts:", error);
    return res.status(500).json({
      success: false,
      message: "Server error while fetching student contacts.",
      error: error.message,
    });
  }
};
exports.sendMessage = async (req, res) => {
  try {
    const { subject, from_type, description, receive_user_id } = req.body;

    // Validate input
    if (!subject || !description) {
      return res.status(400).json({
        success: false,
        message: "Both subject and message are required!",
      });
    }

    let targetUserId;

    if (from_type === "student") {
      // Find the student's supervisor
      const studentDetails = await StudentSupervisor.findOne({
        where: { student_user_id: req.user.id },
        raw: true,
      });

      if (!studentDetails?.lecturer_user_id) {
        return res.status(400).json({
          success: false,
          message: "You don't have a supervisor assigned.",
        });
      }

      targetUserId = studentDetails.lecturer_user_id;
    } else {
      // Lecturer sending message to a student
      targetUserId = receive_user_id;
    }

    // Create the interaction record
    const interraction = await Interractions.create({
      subject,
      from_type,
      message: description,
      send_user_id: req.user.id,
      receive_user_id: targetUserId,
    });

    // Send email and log activity
    await Promise.all([
      sendContactMail(req.user.id, interraction, from_type),
      Activity("Message", `Your supervisor sent you a message`, targetUserId),
    ]);

    return res.status(200).json({
      success: true,
      message: "Message sent successfully.",
      interraction,
    });
  } catch (error) {
    console.error("Error sending message:", error);
    return res.status(500).json({
      success: false,
      message: "An error occurred while sending message.",
      error: error.message,
    });
  }
};

exports.changePassword = async (req, res) => {
  try {
    const { PasswordCurrent, NewPassword, confirmNewPassword, username } = req.body;

    // Validate inputs
    if (!PasswordCurrent || !NewPassword || !confirmNewPassword) {
      return res.status(400).json({
        success: false,
        message: "All fields are required.",
      });
    }

    if (NewPassword !== confirmNewPassword) {
      return res.status(409).json({
        success: false,
        message: "New passwords do not match.",
      });
    }

    // Find user
    const user = await User.findByPk(req.user.id);
    if (!user) {
      return res.status(404).json({
        success: false,
        message: "User not found.",
      });
    }

    // Decrypt existing password
    const decrypted = CryptoJS.AES.decrypt(user.password, key, { iv }).toString(CryptoJS.enc.Utf8);
    if (decrypted !== PasswordCurrent) {
      return res.status(401).json({
        success: false,
        message: "Current password is incorrect.",
      });
    }

    // Encrypt new password
    const newEncrypted = CryptoJS.AES.encrypt(NewPassword, key, { iv }).toString();

    // Update user password
    await User.update({ password: newEncrypted }, { where: { id: req.user.id } });

    // Log activity (non-blocking)
    Activity("Security", "You changed your password", username).catch(console.error);

    return res.status(200).json({
      success: true,
      message: "Password changed successfully.",
    });
  } catch (error) {
    console.error("Password change error:", error);
    return res.status(500).json({
      success: false,
      message: "An error occurred while changing the password.",
      error: error.message,
    });
  }
};
exports.uploadPassport = async (req, res) => {
  try {

    if (!req.file) {
      return res.status(400).json({
        success: false,
        message: "Please upload a valid image file.",
      });
    }

    const user = await User.findOne({ where: { id: req.user.id } });
    if (!user) {
      return res.status(404).json({
        success: false,
        message: "User not found.",
      });
    }

    // Update profile photo path
    const photoPath = req.file.filename;
    const [updated] = await Profile.update(
      { photo_path: photoPath },
      { where: { user_id: user.id } }
    );

    if (!updated) {
      return res.status(500).json({
        success: false,
        message: "Failed to update passport photo.",
      });
    }

    // Log user activity (non-blocking)
    Activity("Profile Update", "Passport photo updated", req.user.id ).catch(console.error);

    return res.status(200).json({
      success: true,
      message: "Passport uploaded successfully.",
      data: {
        photo_path: photoPath,
      },
    });
  } catch (error) {
    console.error("Upload Passport Error:", error);
    return res.status(500).json({
      success: false,
      message: "An error occurred while uploading passport.",
      error: error.message,
    });
  }
};
exports.updateProfile = async (req, res) => {
  try {
    const { firstname, lastname, middlename, title, email, phone } = req.body;
    const userId = req.user?.id;

    // ✅ Validate authentication
    if (!userId) {
      return res.status(401).json({
        success: false,
        message: "Unauthorized access.",
      });
    }

    // ✅ Validate required fields
    if (!firstname || !lastname) {
      return res.status(400).json({
        success: false,
        message: "Firstname and lastname are required.",
      });
    }

    // ✅ Fetch user & profile
    const user = await User.findByPk(userId);
    const profile = await Profile.findOne({ where: { user_id: userId } });

    if (!user || !profile) {
      return res.status(404).json({
        success: false,
        message: "User profile not found.",
      });
    }

    // ✅ Update User table (email & phone)
    await user.update({
      email: email || user.email,
      phone: phone || user.phone,
    });

    // ✅ Update Profile table (personal info)
    await profile.update({
      firstname,
      lastname,
      middlename: middlename || null,
      title: title || profile.title,
    });

    // ✅ Fetch updated profile with relations
    const updatedProfile = await Profile.findOne({
      where: { user_id: userId },
      include: [
        {
          model: User,
          attributes: ["username", "email", "phone", "role"],
        },
        {
          model: Department,
          attributes: ["id", "name"],
          include: [{ model: Institute, attributes: ["name"] }],
        },
      ],
    });

    // ✅ Log activity (non-blocking)
    Activity("Profile", "Profile updated successfully", req.user.username).catch(console.error);

    return res.status(200).json({
      success: true,
      message: "Profile updated successfully.",
      data: updatedProfile,
    });
  } catch (error) {
    console.error("Error updating profile:", error);
    return res.status(500).json({
      success: false,
      message: "An error occurred while updating profile.",
      error: error.message,
    });
  }
};











const sendNoticeMailProject = async (is_approved, id, message) => {
  try {
    
    const topic = await ProjectTopic.findOne({
      where: { id:id.project_id },
      raw:true
    });
    let user = await User.findOne({
      where: { id: topic.student_user_id },
      include: [{ model: Profile }],
    });
    user = await JSON.parse(JSON.stringify(user))
    let ll = is_approved
    const documenttype = await DocumentCategory.findOne({where:{id:id.document_category_id}, raw:true})
    if (user.email) {
      await noticeEmail(
        "project",
        user.email,  //"adegokegbolahan24@gmail.com",// user.email 
        user.profile.firstname,
        "CORASS: Research Submission Status!",
        "https://corass.kwasu.edu.ng/student-login",
        user,
        topic.topic,
        documenttype,
        false, 
        ll, 
        message
      );
    }
  } catch (error) {
    console.log(error);
  }
};
const sendNoticeMail = async (user_id,  type, is_approved, id, message) => {
  try {
    let user = await User.findOne({
      where: { id: user_id },
      include: [{ model: Profile }],
    });
    user = await JSON.parse(JSON.stringify(user))
    const topic = await ProjectTopic.findOne({
      where: { id },
      raw:true
    });
    let ll = is_approved
    if (user.email) {
      await noticeEmail(
        type,
        user.email, // "adegokegbolahan24@gmail.com", //user.email
        user.profile.firstname,
        "CORASS: Topic Submission!",
        "https://corass.kwasu.edu.ng/student-login",
        user,
        topic.topic,
        {},
        false, 
        ll, 
        message
      );
    }
  } catch (error) {
    console.log(error);
  }
};
const sendContactMail = async (user_id, interraction, type) => {
  try {
    if (type === "student") {
      const user = await User.findOne({
        where: { id: user_id },
        include: [{ model: Profile }],
      });

      const student_lecturer = await StudentSupervisor.findOne({
        where: { student_user_id: user_id },
        raw: true,
      });

      if (!student_lecturer) return;

      const lecturer = await Profile.findOne({
        where: { user_id: student_lecturer.lecturer_user_id },
        raw: true,
      });

      const lectureruser = await User.findOne({
        where: { id: student_lecturer.lecturer_user_id },
        raw: true,
      });

      if (lectureruser.email) {
        await noticeEmail(
          "alertstudent",
          lectureruser.email, //"adegokegbolahan24@gmail.com", //lectureruser.email,
          lecturer.title +
            " " +
            lecturer.firstname +
            " " +
            lecturer.middlename +
            " " +
            lecturer.lastname,
          `CORASS ALERT - ${interraction.subject}`,
          "https://corass.kwasu.edu.ng/staff-login",
          user,
          {},
          {},
          false,
          false,
          "",
          interraction
        );
      }
    } else {
      const user = await User.findOne({
        where: { id: user_id },
        include: [{ model: Profile }],
      });

      const student = await Profile.findOne({
        where: { user_id: interraction.receive_user_id },
        raw: true,
      });

      const studentuser = await User.findOne({
        where: { id: interraction.receive_user_id },
        raw: true,
      });
      if (studentuser.email) {
        await noticeEmail(
          "alertstaff",
          studentuser.email, //"adegokegbolahan24@gmail.com", //studentuser.email
          student.firstname + " " + student.middlename + " " + student.lastname,
          `CORASS ALERT - ${interraction.subject}`,
          type == "student"
            ? "https://corass.kwasu.edu.ng/staff-login"
            : "https://corass.kwasu.edu.ng/student-login",
          user,
          {},
          {},
          false,
          false,
          "",
          interraction
        );
      }
    }
  } catch (error) {
    console.log(error);
  }
};