/*
 * Copyright (C) 2018-2020 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {Logger, AddLogLevel, Colorize} from '@openstapps/logger';
import {Command} from 'commander';
import {readFileSync} from 'fs';
import path from 'path';
import {Api, ApiRequestOptions} from './api.js';
import {Issue, IssueState, MembershipScope, Scope} from './types.js';

Logger.setTransformations([new AddLogLevel(), new Colorize()]);

// eslint-disable-next-line unicorn/prefer-module
const packageJson = JSON.parse(readFileSync(path.join(__dirname, '..', 'package.json')).toString());

const commander = new Command('openstapps-gitlab-api');

commander.version(packageJson.version);

commander
  .option('-t, --token [token]', 'GitLab API token', process.env.GITLAB_PRIVATE_TOKEN)
  .option('-u, --url [url]', 'GitLab API URL', 'https://gitlab.com/api/v4/');

commander.command('request <call> [method] [data]').action(async (call, method, data) => {
  const options: ApiRequestOptions = {};

  if (method !== 'GET') {
    options.method = method;
  }

  if (data !== undefined) {
    options.data = JSON.parse(data);
  }

  const api = new Api(commander.getOptionValue('url'), commander.getOptionValue('token'));

  const result = await api.makeGitLabAPIRequest(call, options);

  // eslint-disable-next-line no-console
  console.log(result);
});

commander.command('batch-process <projectId> <action>').action(async (projectId, action) => {
  if (!['close'].includes(action)) {
    await Logger.error('Only "close" is supported as action.');
  }

  const api = new Api(commander.getOptionValue('url'), commander.getOptionValue('token'));

  const issues = (await api.makeGitLabAPIRequest(`/projects/${projectId}/issues?state=opened`, {
    retryOnAnyError: true,
    tries: 10,
  })) as Issue[];

  Logger.log(`Fetched ${issues.length} issue(s).`);

  for (const issue of issues) {
    if (action === 'close') {
      Logger.info(`Closing issue #${issue.iid} of project '${projectId}': ${issue.title}.`);

      await api.makeGitLabAPIRequest(`/projects/${projectId}/issues/${issue.iid}`, {
        data: {
          state_event: 'close',
        },
        method: 'PUT',
        retryOnAnyError: true,
        tries: 10,
      });
    }
    Logger.info(`Processed issue #${issue.iid} of project '${projectId}': ${issue.title}`);
  }

  Logger.ok('Processed all issues.');
});

commander
  .command('copy <projectId> <targetUrl> <targetToken> <targetProjectId>')
  .action(async (projectId, targetUrl, targetToken, targetProjectId) => {
    const api = new Api(commander.getOptionValue('url'), commander.getOptionValue('token'));
    const targetApi = new Api(targetUrl, targetToken);

    // get all issues from project
    const issues = (await api.makeGitLabAPIRequest(`/projects/${projectId}/issues`, {
      retryOnAnyError: true,
      tries: 10,
    })) as Issue[];

    // sort issues by their project specific ids
    issues.sort((a, b) => {
      return a.iid - b.iid;
    });

    // get members of target project
    const members = await targetApi.getMembers(MembershipScope.PROJECTS, targetProjectId);

    let index = 0;

    for (const issue of issues) {
      // get notes of old issue
      const notes = await api.getNotes(projectId, issue);

      // create new issue
      const newIssue = await targetApi.createIssue(
        targetProjectId,
        issue.title,
        issue.description === null
          ? '---'
          : `${issue.web_url}

${issue.description}`,
      );

      for (const note of notes) {
        // skip system notes
        if (note.system) {
          continue;
        }

        // create new note in new issue for every note in issue
        await targetApi.createNote(
          targetProjectId,
          Scope.ISSUES,
          newIssue.iid,
          `**${note.author.name} (@${note.author.username}):**

${note.body}`,
        );
      }

      // close newly created issue if original is closed to
      if (issue.state === IssueState.CLOSED) {
        await targetApi.makeGitLabAPIRequest(`/projects/${targetProjectId}/issues/${newIssue.iid}`, {
          data: {
            state_event: 'close',
          },
          method: 'PUT',
          retryOnAnyError: true,
          tries: 10,
        });
      }

      // search for member in target group with same username
      const assignee = members.find(member => {
        if (issue.assignee === null) {
          return false;
        }

        return member.username === issue.assignee.username;
      });

      // set assignee if usernames match
      if (assignee !== undefined) {
        await targetApi.setAssigneeForIssue(newIssue, assignee.id);
      }

      Logger.log(`Finished issue ${++index} of ${issues.length}.`);
    }
  });

commander.parse(process.argv);

const token = commander.getOptionValue('token');
if (typeof token !== 'string' || token.length === 0) {
  Logger.warn(
    'You probably want to supply a GitLab token either via option or environment variable (GITLAB_PRIVATE_TOKEN).',
  );
}
