import * as semver from "semver";

import { Project } from "../project";
import * as git from "./git";
import { readText, writeText } from "./node-async";

export async function assertUnusedTag(tag: string): Promise<void> {
  if (await git.tagExists(tag)) {
    throw new Error(`Tag ${tag} already exists`);
  }
}

export function getVersionTag(version: string): string {
  return `v${version}`;
}

export function getVersionMessage(version: string): string {
  return `Release v${version}`;
}

export async function commitVersion(version: string, projectRoot?: string): Promise<void> {
  const tag: string = getVersionTag(version);
  const message: string = getVersionMessage(version);
  await git.execGit("add", ["."]);
  await git.execGit("commit", ["-m", message]);
  await git.execGit("tag", ["-a", tag, "-m", message]);
}

export async function release(version: string, locations: Project): Promise<void> {
  await Promise.all([
    assertUnusedTag(getVersionTag(version)),
    git.assertCleanBranch(["master", getVersionTag(version)]),
  ]);
  await setPackageVersion(version, locations);
  await commitVersion(version, locations.root);
}

export interface PackageJson {
  version: string;
  main: string;
  types: string;
  module?: string;
  script?: any;
  gitHead?: string;
}

export async function readJsonFile<T = any>(filePath: string): Promise<T> {
  return JSON.parse(await readText(filePath));
}

export async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {
  return writeText(filePath, JSON.stringify(data, null, 2) + "\n");
}

export async function readPackage(locations: Project): Promise<PackageJson> {
  return readJsonFile<PackageJson>(locations.packageJson);
}

export async function writePackage(pkg: PackageJson, locations: Project): Promise<void> {
  return writeJsonFile(locations.packageJson, pkg);
}

export async function setPackageVersion(version: string, locations: Project): Promise<void> {
  const packageData: PackageJson = await readPackage(locations);
  packageData.version = version;
  return writePackage(packageData, locations);
}

export async function getNextVersion(
  bumpKind: "major" | "minor" | "patch",
  locations: Project,
): Promise<string> {
  const packageData: PackageJson = await readPackage(locations);
  const result: string | null = semver.inc(packageData.version, bumpKind);
  if (typeof result !== "string") {
    throw new Error("FailedAssertion: Unable to increment package version");
  }
  return result;
}

export async function bumpVersion(bumpKind: "major" | "minor" | "patch", locations: Project): Promise<void> {
  const nextVersion: string = await getNextVersion(bumpKind, locations);
  await release(nextVersion, locations);
}
