function Get-XcodeRootPath {
    Param (
        [Parameter(Mandatory)]
        [string] $Version
    )

    return "/Applications/Xcode_$Version.app"
}

function Get-DefaultXcodeRootPath {
    return (Get-Item -Path "/Applications/Xcode.app").Target
}

function Get-XcodeToolPath {
    param (
        [Parameter(ParameterSetName = 'Version')]
        [string] $Version,
        [Parameter(ParameterSetName = 'Path')]
        [string] $XcodeRootPath,
        [string] $ToolName
    )

    if ($PSCmdlet.ParameterSetName -eq "Version") {
        $XcodeRootPath = Get-XcodeRootPath $Version
    }

    return Join-Path $XcodeRootPath "Contents/Developer/usr/bin" $ToolName
}

function Get-XcodeVersionInfo {
    param (
        [Parameter(Mandatory)]
        [string] $XcodeRootPath
    )

    $xcodebuildPath = Get-XcodeToolPath -XcodeRootPath $XcodeRootPath -ToolName "xcodebuild"
    [string]$output = Invoke-Expression "$xcodebuildPath -version"
    $versionOutputParts = $output.Split(" ")

    return @{
        Version = [System.Version]::Parse($versionOutputParts[1])
        Build = $versionOutputParts[4]
    }
}

function Switch-Xcode {
    param (
        [Parameter(ParameterSetName = 'Version')]
        [string] $Version,
        [Parameter(ParameterSetName = 'Path')]
        [string] $XcodeRootPath
    )

    if ($PSCmdlet.ParameterSetName -eq "Version") {
        $XcodeRootPath = Get-XcodeRootPath $Version
    }

    Write-Verbose "Switching Xcode to '${XcodeRootPath}'"
    Invoke-Expression "sudo xcode-select --switch ${XcodeRootPath}"
}

function Test-XcodeStableRelease {
    param (
        [Parameter(ParameterSetName = 'Version')]
        [string] $Version,
        [Parameter(ParameterSetName = 'Path')]
        [string] $XcodeRootPath
    )

    if ($PSCmdlet.ParameterSetName -eq "Version") {
        $XcodeRootPath = Get-XcodeRootPath $Version
    }

    $licenseInfoPlistPath = Join-Path $XcodeRootPath "Contents" "Resources" "LicenseInfo.plist"
    $releaseType = & defaults read $licenseInfoPlistPath "licenseType"

    return -not ($releaseType -match "beta")
}

function Get-XcodeSimulatorsInfo {
    param (
        [string] $Filter
    )

    [string]$rawSimulatorsInfo = Invoke-Expression "xcrun simctl list --json"
    $jsonSimulatorsInfo = $rawSimulatorsInfo | ConvertFrom-Json

    if ($Filter) {
        return $jsonSimulatorsInfo | Select-Object -ExpandProperty $Filter
    }

    return $jsonSimulatorsInfo
}

function Get-XcodeDevicesList {
    $result = @()

    $runtimes = Get-XcodeSimulatorsInfo -Filter "devices"
    $runtimes.PSObject.Properties | ForEach-Object {
        $runtimeName = $_.Name
        $devices = $_.Value
        $devices | Where-Object {
            $availability = $_.availability
            $isAvailable = $_.isAvailable
            return (($availability -eq "(available)") -or ($isAvailable -eq "YES") -or ($isAvailable -eq $true))
        } | ForEach-Object {
            $deviceName = $_.name
            $result += "$runtimeName $deviceName"
        }
    }

    return $result
}

function Get-XcodePairsList {
    $result = @()

    $runtimes = Get-XcodeSimulatorsInfo -Filter "pairs"
    $runtimes.PSObject.Properties | Where-Object {
        return $_.Value.state -match "active"
    } | ForEach-Object {
        $watchName = $_.Value.watch.name
        $phoneName = $_.Value.phone.name
        $result += "$watchName $phoneName"
    }

    return $result
}

#Helper function for execution of xcversion due to: https://github.com/fastlane/fastlane/issues/18161
function Invoke-XCVersion {
    param (
        [Parameter(Mandatory)]
        [string] $Arguments,
        [Parameter()]
        [int] $RetryAttempts = 7,
        [Parameter()]
        [int] $PauseDurationSecs = 1
    )

    $Command = "xcversion $Arguments"
    Write-Host "Execute [$Command]"
    for ($Attempt=1; $Attempt -le $RetryAttempts; $Attempt++) {
        Write-Host "Current attempt: [$Attempt]"
        $result = Get-CommandResult -Command $Command -Multiline
        Write-Host "Exit code: [$($result.ExitCode)]"
        Write-Host "Output message: "
        $result.Output | ForEach-Object { Write-Host $_ }
        if ($result.ExitCode -ne 0) {
            Start-Sleep -Seconds $PauseDurationSecs
        } else {
            return $result.Output
        }
    }
    if ($result.ExitCode -ne 0) {
        throw "Command [$Command] has finished with non-zero exit code."
    }
}

function Get-BrokenXcodeSimulatorsList {
    return @(
        # tvOS Simulators
        @{
            SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-2";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-4";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-16-0";
            XcodeVersion = "14.2"
        },
        # watchOS-8-0 Simulators
        @{
            SimulatorName = "Apple Watch Series 5 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple Watch Series 5 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 41mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 45mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0";
            XcodeVersion = "13.1"
        },
        # watchOS-8-3 Simulators
        @{
            SimulatorName = "Apple Watch Series 5 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple Watch Series 5 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 41mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 45mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3";
            XcodeVersion = "13.2.1"
        },
        # watchOS-8-5 Simulators
        @{
            SimulatorName = "Apple Watch Series 5 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple Watch Series 5 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 40mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple Watch Series 6 - 44mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 41mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        @{
            SimulatorName = "Apple Watch Series 7 - 45mm"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5";
            XcodeVersion = "13.4.1"
        },
        # watchOS-9-0 Simulators
        @{
            SimulatorName = "Apple Watch SE (40mm) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-40mm-2nd-generation";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0";
            XcodeVersion = "14.2"
        },
        @{
            SimulatorName = "Apple Watch SE (44mm) (2nd generation)"
            DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-44mm-2nd-generation";
            RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0";
            XcodeVersion = "14.2"
        }
    )
}
