import express from 'express';
import { PrismaClient } from '@prisma/client'; // Import Prisma Client
import { getMantisSettings, saveTicketToDatabase } from '../services/mantisDownloader.js';
import axios from 'axios';
import reader from '@kenjiuno/msgreader';
import { askGemini } from '../utils/gemini.js';
import { usernameMap } from '../services/mantisSummarizer.js';
const MsgReader = reader.default;

const prisma = new PrismaClient(); // Instantiate Prisma Client
const router = express.Router();

// Helper function to fetch distinct values
const getDistinctValues = async(field, res) =>
{
  try
  {
    const values = await prisma.mantisIssue.findMany({
      distinct: [field],
      select: {
        [field]: true,
      },
      where: { // Exclude null values if necessary
        NOT: {
          [field]: ''
        }
      },
      orderBy: {
        [field]: 'asc',
      },
    });
    res.json(values.map(item => item[field]));
  }
  catch (error)
  {
    console.error(`Error fetching distinct ${field} values:`, error.message);
    res.status(500).json({ error: `Failed to fetch distinct ${field} values` });
  }
};

// GET /mantis/filters/statuses - Fetch unique status values
router.get('/filters/statuses', async(req, res) =>
{
  await getDistinctValues('status', res);
});

// GET /mantis/filters/priorities - Fetch unique priority values
router.get('/filters/priorities', async(req, res) =>
{
  await getDistinctValues('priority', res);
});

// GET /mantis/filters/severities - Fetch unique severity values
router.get('/filters/severities', async(req, res) =>
{
  await getDistinctValues('severity', res);
});

// GET /mantis/filters/reporters - Fetch unique reporter usernames
router.get('/filters/reporters', async(req, res) =>
{
  await getDistinctValues('reporterUsername', res);
});

// GET /mantis - Fetch multiple Mantis issues with filtering and pagination
router.get('/', async(req, res) =>
{
  const { page = 1, limit = 10, status, priority, severity, reporterUsername, search, sortBy = 'updatedAt', sortOrder = 'desc' } = req.query; // Add sortBy and sortOrder

  const pageNum = parseInt(page, 10);
  const limitNum = parseInt(limit, 10);
  const skip = (pageNum - 1) * limitNum;

  const where = {};
  if (status) where.status = status;
  if (priority) where.priority = priority;
  if (severity) where.severity = severity;
  if (reporterUsername) where.reporterUsername = reporterUsername;
  if (search)
  {
    where.OR = [
      { title: { contains: search, mode: 'insensitive' } },
      { description: { contains: search, mode: 'insensitive' } },
      { comments: { some: { comment: { contains: search, mode: 'insensitive' } } } }, // Search in comments
    ];

    // If the search term is a number, treat it as an ID
    const searchNum = parseInt(search, 10);
    if (!isNaN(searchNum))
    {
      where.OR.push({ id: searchNum });
    }
  }

  // Validate sortOrder
  const validSortOrder = ['asc', 'desc'].includes(sortOrder) ? sortOrder : 'desc';

  // Define allowed sort fields to prevent arbitrary sorting
  const allowedSortFields = ['id', 'title', 'status', 'priority', 'severity', 'reporterUsername', 'createdAt', 'updatedAt'];
  const validSortBy = allowedSortFields.includes(sortBy) ? sortBy : 'updatedAt';

  const orderBy = {};
  orderBy[validSortBy] = validSortOrder;

  try
  {
    let [issues, totalCount] = await prisma.$transaction([
      prisma.mantisIssue.findMany({
        where,
        skip,
        take: limitNum,
        orderBy: orderBy, // Use dynamic orderBy
        // You might want to include related data like comments count later
        // include: { _count: { select: { comments: true } } }
      }),
      prisma.mantisIssue.count({ where }),
    ]);

    if (!issues || issues.length === 0)
    {
      //If it's numeric, try to download the issue from Mantis
      const searchNum = parseInt(search, 10);

      if (!isNaN(searchNum))
      {
        let data;
        try
        {
          data = await saveTicketToDatabase(searchNum);
        }
        catch (error)
        {
          console.error('Error saving ticket to database:', error.message);
          return res.status(404).json({ error: 'Mantis issue not found' });
        }

        if (!data)
        {
          return res.status(404).json({ error: 'Mantis issue not found' });
        }

        // Fetch the issue again from the database
        issues = await prisma.mantisIssue.findMany({
          where,
          skip,
          take: limitNum,
          orderBy: orderBy, // Use dynamic orderBy here as well
        });

        if (issues.length === 0)
        {
          return res.status(404).json({ error: 'Mantis issue not found' });
        }

        totalCount = await prisma.mantisIssue.count({ where });
      }
    }

    res.json({
      data: issues,
      pagination: {
        total: totalCount,
        page: pageNum,
        limit: limitNum,
        totalPages: Math.ceil(totalCount / limitNum),
      },
    });
  }
  catch (error)
  {
    console.error('Error fetching Mantis issues:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis issues' });
  }
});

// GET /mantis/:id - Fetch a single Mantis issue by ID
router.get('/:id', async(req, res) =>
{
  const { id } = req.params;
  const issueId = parseInt(id, 10);

  if (isNaN(issueId))
  {
    return res.status(400).json({ error: 'Invalid issue ID format' });
  }

  try
  {
    const issue = await prisma.mantisIssue.findUnique({
      where: { id: issueId },
      include: {
        comments: { // Include comments
          include: { attachments: true } // And include attachments for each comment
        }
      }
    });

    if (!issue)
    {
      //Try to download the issue from Mantis
      const data = await saveTicketToDatabase(issueId);

      if (!data)
      {
        return res.status(404).json({ error: 'Mantis issue not found' });
      }

      // Fetch the issue again from the database
      const issue = await prisma.mantisIssue.findUnique({
        where: { id: issueId },
        include: {
          comments: { // Include comments
            include: { attachments: true } // And include attachments for each comment
          }
        }
      });
    }

    res.json(issue);
  }
  catch (error)
  {
    console.error(`Error fetching Mantis issue ${issueId}:`, error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis issue' });
  }
});


router.get('/attachment/:ticketId/:attachmentId', async(req, res) =>
{
  const { url, headers } = await getMantisSettings();

  const { ticketId, attachmentId } = req.params;

  const attachmentUrl = `${url}/issues/${ticketId}/files/${attachmentId}`;

  console.log('Fetching Mantis attachment from URL:', attachmentUrl);

  try
  {
    const response = await axios.get(attachmentUrl, { headers });
    const attachment = response.data.files[0];

    if (!attachment)
    {
      return res.status(404).json({ error: 'Attachment not found' });
    }

    const buffer = Buffer.from(attachment.content, 'base64');
    res.setHeader('Content-Type', attachment.content_type);
    res.setHeader('Content-Disposition', `attachment; filename="${attachment.filename}"`);
    res.setHeader('Content-Length', buffer.length);
    res.send(buffer);
  }
  catch (error)
  {
    console.error('Error fetching Mantis attachment:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis attachment' });
  }
});

router.get('/msg-extract/:ticketId/:attachmentId', async(req, res) =>
{
  const { url, headers } = await getMantisSettings();

  const { ticketId, attachmentId } = req.params;

  const attachmentUrl = `${url}/issues/${ticketId}/files/${attachmentId}`;

  console.log('Fetching Mantis attachment from URL:', attachmentUrl);

  try
  {
    const response = await axios.get(attachmentUrl, { headers });
    const attachment = response.data.files[0];

    if (!attachment)
    {
      return res.status(404).json({ error: 'Attachment not found' });
    }

    const buffer = Buffer.from(attachment.content, 'base64');

    console.log(MsgReader);

    const reader = new MsgReader(buffer);
    const msg = reader.getFileData();

    res.status(200).json(msg);
  }
  catch (error)
  {
    console.error('Error fetching Mantis attachment:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis attachment' });
  }
});

router.get('/msg-extract/:ticketId/:attachmentId/:innerAttachmentId', async(req, res) =>
{
  const { url, headers } = await getMantisSettings();

  const { ticketId, attachmentId, innerAttachmentId } = req.params;

  const attachmentUrl = `${url}/issues/${ticketId}/files/${attachmentId}`;

  console.log('Fetching Mantis attachment from URL:', attachmentUrl);

  try
  {
    const response = await axios.get(attachmentUrl, { headers });
    const attachment = response.data.files[0];

    if (!attachment)
    {
      return res.status(404).json({ error: 'Attachment not found' });
    }

    const buffer = Buffer.from(attachment.content, 'base64');

    const reader = new MsgReader(buffer);
    const msg = reader.getFileData();

    // Find the inner attachment
    const innerAttachment = msg.attachments[innerAttachmentId];

    if (!innerAttachment)
    {
      return res.status(404).json({ error: 'Inner attachment not found' });
    }

    const attachmentData = reader.getAttachment(innerAttachment);

    const innerBuffer = Buffer.from(attachmentData.content, 'base64');
    res.setHeader('Content-Disposition', `attachment; filename="${innerAttachment.fileName}"`);

    res.status(200).send(innerBuffer);
  }
  catch (error)
  {
    console.error('Error fetching Mantis attachment:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis attachment' });
  }
});

// GET /mantis/stats/issues - Get daily count of new Mantis issues (last 7 days)
router.get('/stats/issues', async(req, res) =>
{
  try
  {
    // Calculate the date range (last 7 days)
    const endDate = new Date();
    endDate.setHours(23, 59, 59, 999); // End of today

    const startDate = new Date();
    startDate.setDate(startDate.getDate() - 6); // 7 days ago
    startDate.setHours(0, 0, 0, 0); // Start of the day

    // Query for daily counts of issues created in the last 7 days
    const dailyIssues = await prisma.$queryRaw`
      SELECT
        DATE(created_at) as date,
        COUNT(*) as count
      FROM
        "MantisIssue"
      WHERE
        created_at >= ${startDate} AND created_at <= ${endDate}
      GROUP BY
        DATE(created_at)
      ORDER BY
        date ASC
    `;

    res.status(200).json(dailyIssues);
  }
  catch (error)
  {
    console.error('Error fetching Mantis issue statistics:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis issue statistics' });
  }
});

// GET /mantis/stats/comments - Get daily count of new Mantis comments (last 7 days)
router.get('/stats/comments', async(req, res) =>
{
  try
  {
    // Calculate the date range (last 7 days)
    const endDate = new Date();
    endDate.setHours(23, 59, 59, 999); // End of today

    const startDate = new Date();
    startDate.setDate(startDate.getDate() - 6); // 7 days ago
    startDate.setHours(0, 0, 0, 0); // Start of the day

    // Query for daily counts of comments created in the last 7 days
    const dailyComments = await prisma.$queryRaw`
      SELECT
        DATE(created_at) as date,
        COUNT(*) as count
      FROM
        "MantisComment"
      WHERE
        created_at >= ${startDate} AND created_at <= ${endDate}
      GROUP BY
        DATE(created_at)
      ORDER BY
        date ASC
    `;

    res.status(200).json(dailyComments);
  }
  catch (error)
  {
    console.error('Error fetching Mantis comment statistics:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis comment statistics' });
  }
});

router.get('/summary/:ticketId', async(req, res) =>
{
  const { ticketId } = req.params;

  try
  {
    const ticket = await prisma.mantisIssue.findUnique({
      where: { id: parseInt(ticketId, 10) },
      include: {
        comments: true,
      },
    });

    if (!ticket)
    {
      return res.status(404).json({ error: 'Mantis ticket not found' });
    }

    //We need to change the usernames using the usernameMap
    ticket.reporterUsername = usernameMap[ticket.reporterUsername] || ticket.reporterUsername;
    ticket.comments = ticket.comments.map((comment) =>
    {
      comment.senderUsername = usernameMap[comment.senderUsername] || comment.senderUsername;
      return comment;
    });

    //Ask Gemini to summarize the ticket
    const summary = await askGemini(`Please summarize the following Mantis ticket in the form of a markdown list of bullet points formatted as "[Date] Point" (ensure a newline between each point, format the date as DD/MM/YYY and surround it with square brackets "[]"). Then after your summary, list any outstanding actions as a markdown list in the format "[Name] Action" (again surrounding the name with square brackets).
      Output a heading 6 "Summary:", a newline, the summary, then two newlines, a heading 6 "Actions:" then the actions. Do not wrap the output in a code block.\n\n### Ticket Data ###\n\n` + JSON.stringify(ticket, null, 2));
    res.status(200).json({ summary });
  }
  catch (error)
  {
    console.error('Error fetching Mantis summary:', error.message);
    res.status(500).json({ error: 'Failed to fetch Mantis summary' });
  }
});

export default router;