open System.IO
open System.Text.Json

let parseRuntimeIdentifiersFromRuntimeJsonFile (jsonFilePath : string) =
    use jsonFileStream = File.OpenRead(jsonFilePath)
    use jsonDocument = JsonDocument.Parse(jsonFileStream)

    jsonDocument.RootElement.GetProperty("runtimes").EnumerateObject()
    |> Seq.map (fun element -> element.Name)
    // save sequence as Set because jsonDocument and jsonFileStream 
    // will get disposed after this function call
    |> Set.ofSeq

let parseSupportedBuildRuntimeIdentifiersFromRuntimeCompatiblityJsonFile (jsonFilePath : string) =
    let supportedBaseRIDs = [| "linux-x64"; "linux-arm"; "linux-arm64"; "win-x64"; "win-x86"; "osx-arm64"; "osx-x64"; "win-arm"; "win-arm64";|]
    
    use jsonFileStream = File.OpenRead(jsonFilePath)
    use jsonDocument = JsonDocument.Parse(jsonFileStream)

    jsonDocument.RootElement.EnumerateObject()
    |> Seq.where (fun element -> 
        if element.Name = "rhel.6-x64" then
            false
        else
            let dependsOn =
                element.Value.EnumerateArray() 
                |> Seq.map (fun arrayElement -> arrayElement.GetString())
                |> Seq.toArray
            
            let hasSupportedBaseId =
                dependsOn |> Array.exists (fun value -> (Array.contains value supportedBaseRIDs)) 

            let dependsOnLinuxBionic =
                dependsOn |> Array.contains "linux-bionic"
            
            hasSupportedBaseId && not dependsOnLinuxBionic)
    |> Seq.map (fun element -> element.Name)
    // save sequence as Set because jsonDocument and jsonFileStream 
    // will get disposed after this function call
    |> Set.ofSeq

type Month =
    | January = 1
    | Febuary = 2
    | March = 3
    | April = 4
    | May = 5
    | June = 6
    | July = 7
    | August = 8
    | September = 9
    | October = 10
    | November = 11
    | December = 12

type UbuntuReleaseDate = { Year: uint; Month: Month }

let TrustyTahr = { Year = 2014u; Month = Month.April; }
let XenialXerus = { Year = 2016u; Month = Month.April; }

let isReleaseSmallerThan releaseA releaseB  = 
    (releaseA.Year < releaseB.Year) || 
    (  
        releaseA.Year = releaseB.Year && 
        (int releaseA.Month) < (int releaseB.Month)
    )

let addSixMonths release =
    let zeroBasedReleaseMonth = (int release.Month) - 1
    let offsetFromStartOfReleaseYearInMonths = zeroBasedReleaseMonth + 6
    
    let yearOffset = offsetFromStartOfReleaseYearInMonths / 12
    let month = (offsetFromStartOfReleaseYearInMonths % 12) + 1

    {
        Year = release.Year + (uint yearOffset)
        Month = enum<Month>(month) 
    }

let asDotnetRuntimePlatformId releaseDate =
    let twoDigitYear = releaseDate.Year % 100u
    let monthValue = (int releaseDate.Month)

    $"ubuntu.{twoDigitYear:D2}.{monthValue:D2}"

let dotnetRIDsOf release =
    let dotnetRuntimePlatformId = asDotnetRuntimePlatformId release
    
    let supportedArchitectureSuffixes = 
        if (isReleaseSmallerThan release XenialXerus) then
            [|""; "-arm"; "-x64"; "-x86"|] 
        else
            [|""; "-arm"; "-arm64"; "-x64"; "-x86"|] 

    supportedArchitectureSuffixes
    |> Seq.map (fun architectureSuffix -> dotnetRuntimePlatformId + architectureSuffix)

let ubuntuDotnetRIDsSinceUntil since until =
    since
    |> Seq.unfold (fun release ->
        let isReleaseSmallerThanUntil = 
            isReleaseSmallerThan release until

        if isReleaseSmallerThanUntil then
            let dotnetRIDs = dotnetRIDsOf release
            let nextRelease = addSixMonths release

            Some(dotnetRIDs, nextRelease)
        else
            None)
    |> Seq.concat

let ubuntuDotnetRIDsSinceTrustyTahr =
    // I use a 5 instead of 6 month for a conservative estimation.
    // Depending on when this test will run automatically there may 
    // be no release announced yet.
    // There is no harm if we forget to patch the RID for a devel 
    // ubuntu version within the release cycle, but it is a pain in 
    // the butt to keep a field like 'LatestRelease' up to date or 
    // care about a flaky test 

    let nowInFiveMonth = System.DateTime.Now.AddMonths(5)
    let nowInFiveMonthAsReleaseDate = 
        {
            Year = (uint nowInFiveMonth.Year)
            Month = enum<Month>(nowInFiveMonth.Month)
        }
    
    ubuntuDotnetRIDsSinceUntil TrustyTahr nowInFiveMonthAsReleaseDate
