import { Octokit } from '@octokit/core';
import { setTimeout as setTimeoutPromise } from 'node:timers/promises';
import OpenAI from 'openai';
import { exit } from 'process';
import { logger } from '../server-utils/logger.js';
import { sendDiscordReleaseNotesMessage } from './notification.js';

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

const splitTextIntoHalves = (text: string) => {
  // Split the text into two halves, each less than 2000 characters for discord limit. Split at each '##' Markdown section.
  const sections = text.split('##');
  let idx;
  let firstHalf = '';
  let secondHalf = '';
  for (idx = 0; idx < sections.length; idx++) {
    if (firstHalf.length + sections[idx].length < 2000) {
      firstHalf += '##' + sections[idx];
    } else {
      secondHalf = '##' + sections.slice(idx).join('##');
      break;
    }
  }
  return [firstHalf, secondHalf];
};

export const sendSplitDiscordReleaseNotesMessage = async (text: string) => {
  const [firstHalf, secondHalf] = splitTextIntoHalves(text);

  await sendDiscordReleaseNotesMessage(firstHalf);

  if (secondHalf === '') return;

  await setTimeoutPromise(3000);
  await sendDiscordReleaseNotesMessage(secondHalf);
};

const main = async (from: Date = new Date(new Date().setDate(new Date().getDate() - 7)), to: Date = new Date()) => {
  logger.info('Getting information from last 100 closed PRs...');
  const prResp = await octokit.request('GET /repos/{owner}/{repo}/pulls?state=closed&per_page=100', {
    owner: 'WavTool',
    repo: 'WavTool',
    headers: {
      'X-GitHub-Api-Version': '2022-11-28',
      Accept: 'application/vnd.github.text+json',
    },
  });

  logger.info('Filtering to PRs closed within specified period...');
  const filteredPrs = prResp.data.filter((pr) => {
    if (!pr.merged_at) return false;
    const mergeDate = new Date(pr.merged_at);
    if (from) {
      const fromDate = new Date(from);
      if (mergeDate < fromDate) return false;
    }
    if (to) {
      const toDate = new Date(to);
      if (mergeDate > toDate) return false;
    }
    return true;
  });

  logger.info('Populating PRs with commit information...');
  const prDataPromises = filteredPrs.map(async (pr) => {
    const commitsHref = pr._links.commits.href;
    const commitsResp = await octokit.request('GET ' + commitsHref);
    const commits = commitsResp.data.map(({ commit }) => {
      return {
        author: commit.author.name,
        message: commit.message,
        date: commit.author.date,
      };
    });
    return {
      branch: pr.head?.ref,
      url: pr.url,
      title: pr.title,
      user: pr.user.login,
      body: pr.body_text,
      created_at: pr.created_at,
      closed_at: pr.closed_at,
      merged_at: pr.merged_at,
      commits,
    };
  });

  const prData = await Promise.all(prDataPromises);

  logger.info('Generating release notes...');
  const dateOptions: Intl.DateTimeFormatOptions = { day: 'numeric', month: 'short' };
  const fromDate = from.toLocaleDateString('en-us', dateOptions);
  const toDate = to.toLocaleDateString('en-us', dateOptions);
  const prompt = `
    The following is a JSON of Pull Requests on the codebase of WavTool, a in-browser Digital Audio Workstation that utilises AI functions.
    These PRs are from the dates of ${fromDate} to ${toDate}.
    Please give me a document of release notes regarding the changes that were made to the app based on the PR descriptions, branch names, and commit messages for each PR.
    These notes will be read by users of the app, so avoid mentioning technical details that are not relevant to the user. Use a friendly tone and keep it concise.
    It should be a summary of changes to the app that users can read without having to fully understand the code level changes that were made to the codebase.
    Focus on the user-facing changes that were made to the app - these are likely to be mentioned in the PR descriptions, or in the PR branch having the word 'feat' in it.
    Ignore updates regarding emails that we send our users.
    The document should be in a markdown format - the text will be saved directly into a markdown file. Only use H1 to H3. Do not use emojis.
    Head the document with 'WavTool Release Notes - {start date} to {end date}', then a paragraph quickly summarizing the changes that were made to the app, and how it will help the user make better music in more fun and creative ways.
    `;

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

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

  logger.info('Sending release notes to Discord...');
  await sendSplitDiscordReleaseNotesMessage(text);

  exit();
};

main();
