import { addDays, format } from 'date-fns';
import { dateToString } from '../../next-res/lib/date.js';
import { sendDiscordStandupMessage } from '../cron/notification.js';
import query from '../server-utils/query.js';
import { declareHandler } from '../server-utils/routesHandler.js';
import OpenAI from 'openai';
import { logger } from '../server-utils/logger.js';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

const MOOD_LIST = [
  {
    mood: 'none',
    emoji: '❓',
  },
  {
    mood: 'happy',
    emoji: '😃',
  },
  {
    mood: 'sad',
    emoji: '😢',
  },
  {
    mood: 'neutral',
    emoji: '😐',
  },
  {
    mood: 'angry',
    emoji: '😡',
  },
  {
    mood: 'excited',
    emoji: '😁',
  },
  {
    mood: 'tired',
    emoji: '😴',
  },
  {
    mood: 'stressed',
    emoji: '😫',
  },
  {
    mood: 'confused',
    emoji: '😕',
  },
  {
    mood: 'relaxed',
    emoji: '😌',
  },
  {
    mood: 'awkward',
    emoji: '🥲',
  },
  {
    mood: 'skeptical',
    emoji: '🤨',
  },
  {
    mood: 'nerdy',
    emoji: '🤓',
  },
  {
    mood: 'partying',
    emoji: '🥳',
  },
  {
    mood: 'disguised',
    emoji: '🥸',
  },
  {
    mood: 'anxious',
    emoji: '🫣',
  },
  {
    mood: 'shocked',
    emoji: '😱',
  },
  {
    mood: 'mind_blown',
    emoji: '🤯',
  },
  {
    mood: 'saluting',
    emoji: '🫡',
  },
  {
    mood: 'cowboy',
    emoji: '🤠',
  },
  {
    mood: 'clown',
    emoji: '🤡',
  },
  {
    mood: 'poop',
    emoji: '💩',
  },
  {
    mood: 'dead',
    emoji: '💀',
  },
  {
    mood: 'rock_on',
    emoji: '🤟',
  },
  {
    mood: 'angry_cat',
    emoji: '😾',
  },
  {
    mood: 'curious',
    emoji: '👀',
  },
  {
    mood: 'heartbroken',
    emoji: '💔',
  },
  {
    mood: 'okay',
    emoji: '🆗',
  },
  {
    mood: 'red_flag',
    emoji: '🚩',
  },
];

export const getWeekStandups = async (date: Date) => {
  let dateObject = date;
  const standups = [];

  // Add 7 days to date object
  dateObject = addDays(dateObject, 7);

  for (let i = 1; i <= 7; i++) {
    const currentDateObject = addDays(dateObject, -i);
    const standupQuery = await query(
      `
      SELECT
        w.id,
        u.id as user_id,
        split_part(u.email, '@', 1) as name,
        w.date,
        w.standup_done,
        w.standup_doing,
        w.standup_blockers,
        w.standup_mood,
        w.is_working
      FROM wavtool_standups w
      JOIN users u ON w.user_id = u.id
      WHERE w.date = $1`,
      [dateToString(currentDateObject)]
    );

    standups.push({
      date: dateToString(currentDateObject),
      standups: standupQuery.rows.map((s) => ({
        id: s.id,
        user_id: s.user_id,
        name: s.name,
        date: dateToString(s.date),
        standup_done: s.standup_done,
        standup_doing: s.standup_doing,
        standup_blockers: s.standup_blockers,
        standup_mood: s.standup_mood,
        is_working: s.is_working,
      })),
    });
  }

  return standups;
};

export const summariseWeekStandups = async (date: Date, sendToDiscord: boolean = true) => {
  const standups = await getWeekStandups(date);

  // Sort standups by date
  standups.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());

  if (!standups.length) {
    return null;
  }

  logger.info('Summarising standups from ', standups[0].date, ' to ', standups[standups.length - 1].date);

  const prompt = `
    The following is a JSON of the standups for the week of ${standups[0].date} to ${
      standups[standups.length - 1].date
    }:
    Please give me a summary of the work in 3 sections, work completed, planned but not completed, and blockers that weren't resolved.
    Each section should be in this format:
    - Point 1
    - Point 2
    - Point 3
    // Note that you should only include the top 5 points for each section.
    *Paragraph summarising the section that can also include items not in the points above.*
    Do not split it out by employee as it is a team summary.
    Head the document with 'Weekly Roundup for {start date} to {end date}' with a h1. Each section should be a h2 followed by normal body text. Do not use emojis in the report. Just give a factual report, there is no need to upsell the team. This is for internal use only.
    Right at the bottom, give a summary of the mood of the team for the week. You can use emojis for this part.
    If the team has not completed any work, please state that no work was done.
  `;

  const chatCompletion = await openai.chat.completions.create({
    messages: [
      { role: 'user', content: prompt },
      { role: 'user', content: JSON.stringify(standups) },
    ],
    model: 'gpt-4o',
  });

  const text = chatCompletion.choices.map((choice) => choice.message.content).join();

  if (sendToDiscord) {
    const paragraphs = text.split('\n#');
    for (let i = 1; i < paragraphs.length; i++) {
      await sendDiscordStandupMessage('#' + paragraphs[i], 'Summarybot');
    }
  }

  // Save summary to the database
  await query(
    `
    INSERT INTO wavtool_standup_summaries
      (date_from, date_to, summary)
      VALUES ($1, $2, $3)`,
    [standups[0].date, standups[standups.length - 1].date, text]
  );

  return text;
};

export const summariseWeekStandupsHandler = declareHandler({
  func: async (req, res) => {
    // Get selected date
    const { date } = req.params;

    if (!date) {
      return res.status(400).send({
        message: 'Missing required parameters',
      });
    }

    // Make sure date is a valid date
    let dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      return res.status(400).send({
        message: 'Invalid date',
      });
    }

    const summary = await summariseWeekStandups(dateObject);

    return res.status(200).send({
      data: summary,
    });
  },
});

export const getWeekStandupSummary = declareHandler({
  func: async (req, res) => {
    // Get selected date
    const { date } = req.params;

    if (!date) {
      return res.status(400).send({
        message: 'Missing required parameters',
      });
    }

    // Make sure date is a valid date
    let dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      return res.status(400).send({
        message: 'Invalid date',
      });
    }

    // Get date 6 days later
    const dateObjectEnd = addDays(dateObject, 6);

    const summaryQuery = await query(
      `
      SELECT summary
      FROM wavtool_standup_summaries
      WHERE date_from = $1 AND date_to = $2
      ORDER BY created_at DESC LIMIT 1
      `,
      [dateToString(dateObject), dateToString(dateObjectEnd)]
    );

    return res.status(200).send({
      data: summaryQuery.rows.length ? summaryQuery.rows[0].summary : null,
    });
  },
});

export const getUserStandup = declareHandler({
  func: async (req, res) => {
    const { userId, date } = req.params;
    if (!userId || !date) {
      return res.status(400).send({
        message: 'Missing required parameters',
      });
    }

    // Make sure date is a valid date
    const dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      return res.status(400).send({
        message: 'Invalid date',
      });
    }

    const standup = await query(
      `
      SELECT
        id,
        date,
        is_working,
        standup_done,
        standup_doing,
        standup_blockers,
        standup_mood
      FROM wavtool_standups WHERE user_id = $1 AND date = $2`,
      [userId, dateObject]
    );

    // Get the latest standup that is before the selected date
    const previousStandup = await query(
      `
      SELECT
        id,
        date,
        standup_done,
        standup_doing,
        standup_blockers,
        standup_mood
      FROM wavtool_standups WHERE user_id = $1 AND date < $2 AND is_working=true
      ORDER BY date DESC
      LIMIT 1`,
      [userId, dateObject]
    );

    if (standup.rows.length || previousStandup.rows.length) {
      return res.status(200).send({
        data: {
          currentStandup: standup.rows.length
            ? {
                ...standup.rows[0],
                date: dateToString(standup.rows[0].date),
              }
            : null,
          previousStandup: previousStandup.rows.length
            ? { ...previousStandup.rows[0], date: dateToString(previousStandup.rows[0].date) }
            : null,
        },
      });
    } else {
      return res.status(404).send({
        data: null,
        message: 'Standup not found',
      });
    }
  },
});

export const upsertUserStandup = declareHandler({
  func: async (req, res) => {
    // Get data from body
    const { userId, date, standup_done, standup_doing, standup_blockers, standup_mood, is_working } = req.body;
    if (!userId || !date) {
      return res.status(400).send({
        message: 'Missing required parameters',
      });
    }

    // Check if valid date
    const dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      return res.status(400).send({
        message: 'Invalid date',
      });
    }

    // Check if standup exists
    const standup = await query(
      `
      SELECT id
      FROM wavtool_standups WHERE user_id = $1 AND date = $2`,
      [userId, dateObject]
    );

    if (standup.rows.length > 0) {
      // Update standup
      await query(
        `
        UPDATE wavtool_standups
        SET
          standup_done = $1,
          standup_doing = $2,
          standup_blockers = $3,
          standup_mood = $4,
          is_working = $5
        WHERE id = $6`,
        [standup_done, standup_doing, standup_blockers, standup_mood, is_working, standup.rows[0].id]
      );
    } else {
      // Insert new standup
      await query(
        `
        INSERT INTO wavtool_standups
          (user_id, date, standup_done, standup_doing, standup_blockers, standup_mood, is_working)
          VALUES ($1, $2, $3, $4, $5, $6, $7)`,
        [userId, date, standup_done, standup_doing, standup_blockers, standup_mood, is_working]
      );

      const userQuery = await query(`SELECT email FROM users WHERE id = $1;`, [userId]);
      const name = userQuery.rows[0].email.split('@')[0];
      const formalName = name.charAt(0).toUpperCase() + name.slice(1);
      const moodEmoji = MOOD_LIST.find((m) => m.mood === standup_mood).emoji;

      if (is_working) {
        const message = [
          `**Standup for ${formalName} on ${format(new Date(date), 'MMM dd')} (${moodEmoji}):**`,
          '',
          `**What I got done since last standup:**`,
          `${standup_done}`,
          '',
          `**What I'm getting done today:**`,
          `${standup_doing}`,
          '',
          `**Things that could block me:**`,
          `${standup_blockers}`,
        ].join('\n');

        await sendDiscordStandupMessage(message, formalName);
      } else {
        const message = [
          `**Standup for ${formalName} on ${format(new Date(date), 'MMM dd')}:**`,
          '',
          `**I'm not working today 🏝️**`,
        ].join('\n');

        await sendDiscordStandupMessage(message, formalName);
      }
    }

    return res.status(200).send({
      message: 'Standup saved',
    });
  },
});

export const getWeekStandupHandler = declareHandler({
  func: async (req, res) => {
    // Get selected date
    const { date } = req.params;

    if (!date) {
      return res.status(400).send({
        message: 'Missing required parameters',
      });
    }

    // Make sure date is a valid date
    let dateObject = new Date(date);
    if (isNaN(dateObject.getTime())) {
      return res.status(400).send({
        message: 'Invalid date',
      });
    }

    const standups = await getWeekStandups(dateObject);

    return res.status(200).send({
      data: standups,
    });
  },
});
