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

namespace Gdmt.Restore

module Private =
    open System
    open System.IO

    open SimpleLog.SimpleLog

    open Gdmt.Shared
    open Gdmt.Shared.Types

    let LinuxRuntimes: StringList =
        [
          // generic
          "linux"

          // glibc
          "linux-arm"
          "linux-arm64"
          "linux-x64"
          "linux-x86"

          // musl
          "linux-musl-arm"
          "linux-musl-arm64"
          "linux-musl-x64"
          "linux-musl-x86" ]

    let DotnetRuntimeNugets: StringList =
        [ "microsoft.aspnetcore.app.ref"
          "microsoft.aspnetcore.app.runtime."
          "microsoft.net.illink.tasks"
          "microsoft.net.sdk.webassembly.pack"
          "microsoft.netcore.app.host."
          "microsoft.netcore.app.ref"
          "microsoft.netcore.app.runtime." ]

    let FindSdkExecutable (sdkVersion: string) : string =
        let srcExe = $"/usr/bin/dotnet-{sdkVersion}"
        let binExe = $"/usr/bin/dotnet-bin-{sdkVersion}"

        if File.Exists(srcExe) then
            srcExe
        elif File.Exists(binExe) then
            binExe
        else
            "no suitable dotnet sdk executable was found" |> Exception |> raise

    let GetFileDirectory (path: string) =
        match path with
        | d when Directory.Exists d -> d
        | f when File.Exists f -> Path.GetDirectoryName(f)
        | _ -> $"path {path} does not exist" |> Exception |> raise

    let RestoreForRuntime
        (sdkExe: string)
        (runtime: string)
        (projectPath: string)
        (tmpDir: string)
        (nugetPackagesDir: string)
        (extraRestoreArgs: string)
        : bool =

        let env =
            [ ("PWD", GetFileDirectory projectPath)
              ("TMPDIR", tmpDir)
              ("NUGET_PACKAGES", nugetPackagesDir)

              ("DOTNET_CLI_TELEMETRY_OPTOUT", "1")
              ("DOTNET_NOLOGO", "1")
              ("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", "1")
              ("MSBUILDDISABLENODEREUSE", "1")
              ("UseSharedCompilation", "false") ]

        let args =
            [ sdkExe
              "restore"
              "--force-evaluate"
              "--verbosity"
              "quiet"
              "--runtime"
              runtime
              extraRestoreArgs
              projectPath ]

        let execProcess = ExecProcess(args, env)

        execProcess.Run().Success

    let ForEachRuntime (lambdaForRuntime: string -> bool) : unit =
        LinuxRuntimes
        |> List.map (fun runtime ->
            LogMessage Debug $"Performing restore for runtime: {runtime}"

            let exitSuccess = lambdaForRuntime runtime

            (runtime, exitSuccess))
        |> List.map (fun (runtime, exitSuccess) ->
            match exitSuccess with
            | true -> LogMessage Success $"Restore for runtime {runtime} passed successfully"
            | _ -> LogMessage Error $"Restore for runtime {runtime} has failed")
        |> ignore

    let FilterNugets (nugets: StringSequence) =
        nugets
        |> Seq.filter (fun atNuget -> not (DotnetRuntimeNugets |> List.exists atNuget.Contains))

    let MaybeFilterNugets (filter: bool) (nugets: StringSequence) =
        if filter then FilterNugets nugets else nugets

    let GatherNugets (filter: bool) (nugetPackagesDir: string) : StringList =
        try
            Directory.GetDirectories(nugetPackagesDir, "*", SearchOption.AllDirectories)
            |> Seq.map (fun path -> path.Substring(nugetPackagesDir.Length + 1))
            |> Seq.map (fun path -> path.Split('/'))
            |> Seq.filter (fun pathElements -> pathElements.Length = 2)
            |> Seq.map (fun pathElements -> $"{pathElements.[0]}@{pathElements.[1]}")
            |> MaybeFilterNugets filter
            |> Seq.sort
            |> Seq.toList
        with ex ->
            LogMessage Error ex.Message
            []
