// Copyright 2023-2024 Gentoo Authors
// Distributed under the terms of the GNU General Public License v2

open System
open System.IO
open System.Threading.Tasks

open SimpleLog.SimpleLog
open System.CommandLine
open System.CommandLine.Invocation

open Gdmt.Shared
open Gdmt.Shared.Ebuild
open Gdmt.Shared.Git

open Gdmt.Commit.Private

let CommandName = "gdmt-commit"
let CommandDescription = "commit ebuild packages"

// Git / commit message

let DryRunOption = new Option<bool>([| "-d"; "--dry" |], "do not make commits")
DryRunOption.SetDefaultValue false

let GitCommitMessageOption =
    new Option<string>([| "-m"; "--message" |], "commit message")

let GitCommitMessageAuxOption =
    new Option<string>([| "-M"; "--message-aux" |], "auxiliary commit message")

let GitSignOption = new Option<bool>([| "-S"; "--sign" |], "sign the create commit")

GitSignOption.SetDefaultValue false

// Tests

let ScanOption = new Option<bool>([| "-s"; "--scan" |], "scan using pkgcheck")
ScanOption.SetDefaultValue false

let TestOption = new Option<bool>([| "-t"; "--test" |], "test using ebuild")
TestOption.SetDefaultValue false

// Bump

let BumpOption =
    new Option<string>([| "-B"; "--bump" |], "set message to \"bump to VERSION\"")

let BumpCopyOption =
    new Option<bool>([| "-C"; "--copy" |], "if doing a bump copy latest ebuild")

BumpCopyOption.SetDefaultValue false

// Drop

let DropOption =
    new Option<string>([| "-D"; "--drop" |], "set message to \"drop old VERSION\"")

let DropRemoveOption =
    new Option<bool>([| "-R"; "--remove" |], "if doing a drop, remove ebuild")

DropRemoveOption.SetDefaultValue false

// Sync live

let SyncLiveOption =
    new Option<bool>([| "-L"; "--live" |], "copy latest non-9999 into 9999 ebuild")

SyncLiveOption.SetDefaultValue false

// Bugs

let BugOption =
    new Option<string>([| "-b"; "--bug" |], "reference a Gentoo BugZilla or upstream bug/URL")

let ClosesOption =
    new Option<string>([| "-c"; "--closes" |], "closes a Gentoo BugZilla bug")

let CommandHandler (context: InvocationContext) : Task =
    task {
        let options = context.ParseResult

        let dryRun = options.GetValueForOption DryRunOption

        let gitCommitMessage = options.GetValueForOption GitCommitMessageOption
        let gitCommitMessageAux = options.GetValueForOption GitCommitMessageAuxOption
        let gitSign = options.GetValueForOption GitSignOption

        let scan = options.GetValueForOption ScanOption
        let test = options.GetValueForOption TestOption

        let bump = options.GetValueForOption BumpOption
        let bumpCopy = options.GetValueForOption BumpCopyOption
        let drop = options.GetValueForOption DropOption
        let dropRemove = options.GetValueForOption DropRemoveOption
        let syncLive = options.GetValueForOption SyncLiveOption

        let bugSources = options.GetValueForOption BugOption
        let closesSources = options.GetValueForOption ClosesOption

        let mutable commitMessage = ""

        CheckEbuildRepository "."

        // CATEGORY/PN
        let packageFullName = GetFullPackageName "."
        // PN
        let packageShortName = Path.GetFileName packageFullName

        if not dryRun then
            RemoveIndexLock()

        if not (String.IsNullOrEmpty bump) then
            commitMessage <- $"{packageFullName}: bump to {bump}"

            if bumpCopy then
                let latest = GetLatestNonLiveEbuildFile "."
                let updated = $"{packageShortName}-{bump}.ebuild"

                LogMessage Debug $"Copying {latest} into {updated}"
                File.Copy(latest, updated)

                if not dryRun then
                    ExecProcess([ "git"; "add"; updated ]).Run().Check()

        if not (String.IsNullOrEmpty drop) then
            commitMessage <- $"{packageFullName}: drop old {drop}"

            if dropRemove then
                let old = $"{packageShortName}-{drop}.ebuild"

                LogMessage Debug $"Removing ebuild file: {old}"
                File.Delete(old)

        if syncLive then
            commitMessage <- $"{packageFullName}: sync live 9999 version"

            let latest = GetLatestNonLiveEbuildFile "."
            let updated = $"{packageShortName}-9999.ebuild"

            LogMessage Debug $"Copying {latest} into {updated}"
            File.Copy(latest, updated, true)

            if not dryRun then
                ExecProcess([ "git"; "add"; updated ]).Run().Check()

        let changes = GetChangedFiles "../.."
        let changedEbuilds = changes |> List.filter IsEbuildFile

        for change in changes do
            LogMessage Debug $"Detected change in file: {change}"

        for change in changedEbuilds do
            LogMessage Debug $"Ebuild file was modified: {change}"

        UpdateEbuildManifests()

        if not dryRun then
            GitAddAll()

        if scan then
            PkgcheckScan()

        if not (String.IsNullOrEmpty gitCommitMessage) then
            commitMessage <- $"{packageFullName}: {gitCommitMessage}"

        GuardCommitMessage commitMessage

        if test then
            match changedEbuilds with
            | [] -> LogMessage Debug "No changed ebuilds, no testing to do"
            | _ -> TestEbuilds changedEbuilds

        let bugTags =
            match bugSources with
            | s when String.IsNullOrEmpty s -> ""
            | s -> CreateTags "Bug" (s.Split [| ' ' |])

        let closesTags =
            match closesSources with
            | s when String.IsNullOrEmpty s -> ""
            | s -> CreateTags "Closes" (s.Split [| ' ' |])

        let commitMessageFull =
            commitMessage
            + "\n"
            + match gitCommitMessageAux with
              | s when String.IsNullOrEmpty s -> ""
              | s -> "\n" + s + "\n"
            + "\n"
            + bugTags
            + closesTags

        if not dryRun then
            let gitCommitArgs =
                [ "git"; "commit"; "-m"; $"'{commitMessageFull}'" ]
                @ (if gitSign then [ "--signoff" ] else [])

            ExecProcess(gitCommitArgs).Run().Check()

        ()
    }

[<EntryPoint>]
let main argv =
    let rootCommand = RootCommand(CommandName)

    rootCommand.Name <- CommandName
    rootCommand.Description <- CommandDescription

    rootCommand.AddOption DryRunOption

    rootCommand.AddOption GitCommitMessageOption
    rootCommand.AddOption GitCommitMessageAuxOption
    rootCommand.AddOption GitSignOption

    rootCommand.AddOption ScanOption
    rootCommand.AddOption TestOption

    rootCommand.AddOption BumpOption
    rootCommand.AddOption BumpCopyOption
    rootCommand.AddOption DropOption
    rootCommand.AddOption DropRemoveOption
    rootCommand.AddOption SyncLiveOption

    rootCommand.AddOption BugOption
    rootCommand.AddOption ClosesOption

    rootCommand.SetHandler CommandHandler

    rootCommand.Invoke(argv)
