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

open System
open System.Threading.Tasks

open System.CommandLine
open System.CommandLine.Invocation

open Gdmt.Shared
open Gdmt.Shared.Ebuild
open Gdmt.Shared.Types

open Gdmt.Ebuild.Private

let CommandName = "gdmt-ebuild"

let CommandDescription =
    "wrapper to run the ebuild command faster and for multiple files"

// Ebuild command

let EbuildPathArgument =
    new Argument<string>("ebuild-path", "ebuild file path or directory of ebuilds")

EbuildPathArgument.SetDefaultValue "."

let EbuildCommandsArgument =
    new Argument<StringArray>("ebuild-command", "ebuild command(s), e.g. prepare, compile")

EbuildCommandsArgument.SetDefaultValue [| "manifest"; "clean"; "install" |]

// Package manager (Portage)

let PortageEmergeDepsOption =
    new Option<bool>([| "-d"; "--deps" |], "emerge the package dependencies first")

let PortageFeaturesOption =
    new Option<StringArray>([| "-f"; "--features" |], "Portage features")

PortageFeaturesOption.SetDefaultValue [||]

let PortageUseOption =
    new Option<StringArray>([| "-u"; "--use" |], "Portage USE flags")

PortageUseOption.SetDefaultValue [||]

let ForceTestsOption = new Option<bool>([| "-t"; "--test" |], "force running tests")
ForceTestsOption.SetDefaultValue false

// System stress

let NicenessOption = new Option<int>([| "-n"; "--nice" |], "niceness level")
NicenessOption.SetDefaultValue 19

let Nproc = Environment.ProcessorCount

let JobsOption = new Option<int>([| "-j"; "--jobs" |], "max number of jobs")
JobsOption.SetDefaultValue Nproc

let LoadAverageOption =
    new Option<int>([| "-l"; "--load" |], "max number of load average")

LoadAverageOption.SetDefaultValue Nproc

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

        let ebuildPath = options.GetValueForArgument(EbuildPathArgument)
        let ebuildCommands = options.GetValueForArgument(EbuildCommandsArgument)

        let portageEmergeDeps = options.GetValueForOption(PortageEmergeDepsOption)
        let portageFeatures = options.GetValueForOption(PortageFeaturesOption)
        let portageUse = options.GetValueForOption(PortageUseOption)

        let forceTests = options.GetValueForOption(ForceTestsOption)

        let niceness = options.GetValueForOption(NicenessOption)
        let jobs = options.GetValueForOption(JobsOption)
        let loadAverage = options.GetValueForOption(LoadAverageOption)

        CheckEbuildRepository "."

        let ebuildFiles = FindEbuildFiles ebuildPath

        let featuresSting = portageFeatures |> String.concat " "
        let useSting = portageUse |> String.concat " "

        if portageEmergeDeps then
            let packageName = GetFullPackageName "."

            let processArgs =
                [ "sudo"
                  "emerge"
                  "--oneshot"
                  "--onlydeps"
                  "--quiet-build"
                  "--verbose"
                  packageName ]

            ExecProcess(processArgs).Run().Check()

        RunEbuildCommandsForEach
            ebuildFiles
            ebuildCommands
            ({ DoPerformTest = forceTests
               FEATURES = featuresSting
               USE = useSting })
            ({ Niceness = niceness
               Jobs = jobs
               LoadAverage = loadAverage })

        ()
    }

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

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

    rootCommand.AddArgument EbuildPathArgument
    rootCommand.AddArgument EbuildCommandsArgument

    rootCommand.AddOption PortageEmergeDepsOption
    rootCommand.AddOption PortageFeaturesOption
    rootCommand.AddOption PortageUseOption

    rootCommand.AddOption ForceTestsOption

    rootCommand.AddOption NicenessOption
    rootCommand.AddOption JobsOption
    rootCommand.AddOption LoadAverageOption

    rootCommand.SetHandler CommandHandler

    rootCommand.Invoke(argv)
