package autoversion

import autoversion.model.{Commit, Tag}
import autoversion.model.BumpOrdering.bumpOrdering

import sbt._
import com.typesafe.sbt.{GitPlugin, SbtGit}
import com.vdurmont.semver4j.Semver
import com.vdurmont.semver4j.Semver.SemverType
import sbtrelease.{versionFormatError, ReleasePlugin, Version}
import sbtrelease.ReleasePlugin.autoImport.releaseVersion

import scala.util.Properties

object AutoVersionPlugin extends AutoPlugin {

  val autoImport = Keys

  import autoImport._

  override def trigger: PluginTrigger = allRequirements
  override def requires: Plugins      = GitPlugin && ReleasePlugin

  override def projectSettings: Seq[Setting[_]] = Seq(
    tagNameCleaner := { _.stripPrefix("v") },
    bugfixRegexes := List("""\[?bugfix\]?.*""", """\[?fix\]?.*""", """\[?patch\]?.*""").map(_.r),
    minorRegexes := List(".*").map(_.r),
    majorRegexes := List("""\[?breaking\]?.*""", """\[?major\]?.*""").map(_.r),
    latestTag := findLatestTag.value,
    unreleasedCommits := listUnreleasedCommits.value,
    suggestedBump := suggestBump.value,
    releaseVersion := { ver =>
      Version(ver).map(v => v.bump(suggestedBump.value).withoutQualifier.string).getOrElse(versionFormatError)
    }
  )

  private lazy val findLatestTag = Def.task {
    val gitTags  = runGit("tag", "--list").value
    val versions = gitTags.map(tag => Tag(tag, new Semver(tagNameCleaner.value(tag), SemverType.LOOSE)))
    versions.sortBy(_.version).lastOption
  }

  private lazy val listUnreleasedCommits = Def.taskDyn {
    val tag = latestTag.value.map(tag => s"${tag.raw}...").getOrElse("")
    Def.task {
      val commitListOutput = runGit("log", "--oneline", "--no-decorate", "--color=never", s"${tag}HEAD").value
      commitListOutput.map(Commit.apply).toVector
    }
  }

  private lazy val suggestBump = Def.task {
    val commits = unreleasedCommits.value
    val suggestedBumps =
      commits.flatMap(_.suggestedBump(majorRegexes.value, minorRegexes.value, bugfixRegexes.value))
    if (suggestedBumps.isEmpty)
      throw new RuntimeException("No commit matches either patterns for bugfix, minor or major bumps !")
    else suggestedBumps.max
  }

  private def runGit(args: String*) = Def.task {
    SbtGit.GitKeys.gitRunner
      .value(args: _*)(file("."), Logger.Null)
      .split(Properties.lineSeparator)
      .filter(_.trim.nonEmpty)
  }
}