import path from 'path';
import chalk from 'chalk';
import terminalLink from 'terminal-link';

import { GitLog, GitFileLog } from './Git';

const { white, cyan, red, green, yellow, blue, gray, dim, reset } = chalk;

/**
 * Uses `terminal-link` to create clickable link in the terminal.
 * If the terminal doesn't support this feature, fallback just to provided text, it would look ugly on the CI.
 */
export function link(text: string, url: string) {
  return terminalLink(text, url, { fallback: (text) => text });
}

/**
 * Formats an entry from git log.
 */
export function formatCommitLog(log: GitLog): string {
  const authorName = green(log.authorName);
  const commitHash = formatCommitHash(log.hash);
  const title = formatCommitTitle(log.title);
  const date = log.committerRelativeDate;

  return `${commitHash} ${title} ${gray(`by ${authorName} ${date}`)}`;
}

/**
 * Formats commit title. So far it only makes closing PR number a link to the PR on GitHub.
 */
export function formatCommitTitle(title: string): string {
  return title.replace(
    /\(#(\d+)\)$/g,
    `(${blue.bold(link('#$1', 'https://github.com/expo/expo/pull/$1'))})`
  );
}

/**
 * Formats commit hash to display it as a link pointing to the commit on GitHub.
 */
export function formatCommitHash(hash?: string): string {
  if (!hash) {
    return gray('undefined');
  }
  const url = `https://github.com/expo/expo/commit/${hash}`;
  const abbreviatedHash = hash.substring(0, 6);

  return yellow.bold(`(${link(abbreviatedHash, url)})`);
}

/**
 * Formats markdown changelog entry to be displayed nicely in the terminal.
 * Replaces links to terminal chars sequence that prints clickable text pointing to given URL.
 */
export function formatChangelogEntry(entry: string): string {
  return entry
    .replace(/\[(#\d+|@\w+)\]\(([^)]+?)\)/g, blue.bold(link('$1', '$2')))
    .replace(/(\W)([_*]{2})([^\2]*?)\2(\W)/g, '$1' + reset.bold('$3') + '$4')
    .replace(/(\W)([_*])([^\2]*?)\2(\W)/g, '$1' + reset.italic('$3') + '$4')
    .replace(/`([^`]+?)`/g, dim('$1'));
}

/**
 * Formats file log - that is a relative file path and the status (modified, added, etc.).
 */
export function formatFileLog(fileLog: GitFileLog): string {
  const uri = `vscode://file/${fileLog.path}`;
  return `${link(fileLog.relativePath, uri)} ${gray(`(${fileLog.status})`)}`;
}

/**
 * Removes all non-ascii characters (characters with unicode number between `0` and `127` are left untouched) from the string.
 * https://www.utf8-chartable.de/unicode-utf8-table.pl?number=128
 */
export function stripNonAsciiChars(str: string): string {
  return str.replace(/[^\x00-\x7F]/gu, '');
}

/**
 * Makes Xcode logs pretty as xcpretty :)
 */
export function formatXcodeBuildOutput(output: string): string {
  return output
    .replace(/^(\/.*)(:\d+:\d+): (error|warning|note)(:.*)$/gm, (_, p1, p2, p3, p4) => {
      if (p3 === 'note') {
        return gray.bold(p3) + white.bold(p4);
      }
      const relativePath = path.relative(process.cwd(), p1);
      const logColor = p3 === 'error' ? red.bold : yellow.bold;

      return cyan.bold(relativePath + p2) + ' ' + logColor(p3 + p4);
    })
    .replace(/^(In file included from )(\/[^\n]+)(:\d+:[^\n]*)$/gm, (_, p1, p2, p3) => {
      const relativePath = path.relative(process.cwd(), p2);
      return gray.italic(p1 + relativePath + p3);
    })
    .replace(/\s\^\n(\s[^\n]+)?/g, green.bold('$&\n'))
    .replace(/\*\* BUILD FAILED \*\*/, red.bold('$&'));
}
