import GithubAPI from "./GithubAPI.js";
import api from './api.js';
import Handlebars from "handlebars";
import fs from "fs/promises";
import {colorize} from "./helpers/colorize.js";
import {getReleaseInfo} from "./contributors.js";
import path from "path";
import {fileURLToPath} from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const NOTIFY_PR_TEMPLATE = path.resolve(__dirname, '../templates/pr_published.hbs');

const normalizeTag = (tag) => tag ? 'v' + tag.replace(/^v/, '') : '';

const GITHUB_BOT_LOGIN = 'github-actions[bot]';

const skipCollaboratorPRs = true;

class RepoBot {
  constructor(options) {
    const {
      owner, repo,
      templates
    } = options || {};

    this.templates = Object.assign({
      published: NOTIFY_PR_TEMPLATE
    }, templates);

    this.github = api || new GithubAPI(owner, repo);

    this.owner = this.github.owner;
    this.repo = this.github.repo;
  }

  async addComment(targetId, message) {
    return this.github.createComment(targetId, message);
  }

  async notifyPRPublished(id, tag) {
    let pr;

    try {
      pr = await this.github.getPR(id);
    } catch (err) {
      if(err.response?.status === 404) {
        throw new Error(`PR #${id} not found (404)`);
      }

      throw err;
    }

    tag = normalizeTag(tag);

    const {merged, labels, user: {login, type}} = pr;

    const isBot = type === 'Bot';

    if (!merged) {
      return false
    }

    await this.github.appendLabels(id, [tag]);

    if (isBot || labels.find(({name}) => name === 'automated pr') || (skipCollaboratorPRs && await this.github.isCollaborator(login))) {
      return false;
    }

    const comments = await this.github.getComments(id, {desc: true});

    const comment = comments.find(
      ({body, user}) => user.login === GITHUB_BOT_LOGIN && body.indexOf('published in') >= 0
    )

    if (comment) {
      console.log(colorize()`Release comment [${comment.html_url}] already exists in #${pr.id}`);
      return false;
    }

    const author = await this.github.getUser(login);

    author.isBot = isBot;

    const message = await this.constructor.renderTemplate(this.templates.published, {
      id,
      author,
      release: {
        tag,
        url: `https://github.com/${this.owner}/${this.repo}/releases/tag/${tag}`
      }
    });

    return await this.addComment(id, message);
  }

  async notifyPublishedPRs(tag) {
    tag = normalizeTag(tag);

    const release = await getReleaseInfo(tag);

    if (!release) {
      throw Error(colorize()`Can't get release info for ${tag}`);
    }

    const {merges} = release;

    console.log(colorize()`Found ${merges.length} PRs in ${tag}:`);

    let i = 0;

    for (const pr of merges) {
      try {
        console.log(colorize()`${i++}) Notify PR #${pr.id}`)
        const result = await this.notifyPRPublished(pr.id, tag);
        console.log('✔️', result ? 'Label, comment' : 'Label');
      } catch (err) {
        console.warn(colorize('green', 'red')`❌ Failed notify PR ${pr.id}: ${err.message}`);
      }
    }
  }

  static async renderTemplate(template, data) {
    return Handlebars.compile(String(await fs.readFile(template)))(data);
  }
}

export default RepoBot;