[CmdletBinding()]
param()

$ErrorActionPreference = "Stop"

function Get-AndroidSdkRoot {
    $candidates = @(
        $env:ANDROID_SDK_ROOT,
        $env:ANDROID_HOME,
        (Join-Path $env:LOCALAPPDATA "Android\Sdk")
    )

    foreach ($candidate in $candidates) {
        if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Container)) {
            return (Resolve-Path -LiteralPath $candidate).Path
        }
    }

    throw "Android SDK root was not found. Install command-line tools or set ANDROID_SDK_ROOT."
}

function Get-LatestAndroidSdkPackageDirectory {
    param(
        [Parameter(Mandatory=$true)][string]$SdkRoot,
        [Parameter(Mandatory=$true)][string]$PackageName
    )

    $packageRoot = Join-Path $SdkRoot $PackageName
    if (!(Test-Path -LiteralPath $packageRoot -PathType Container)) {
        throw "Android SDK package directory not found: $packageRoot"
    }

    $packages = @(Get-ChildItem -LiteralPath $packageRoot -Directory |
        Where-Object { $_.Name -match '^\d+(\.\d+)*$' } |
        Sort-Object { [version]$_.Name } -Descending)

    if ($packages.Count -eq 0) {
        throw "No installed Android SDK package versions found under $packageRoot"
    }

    return $packages[0]
}

function Get-AndroidSdkManagerCommand {
    param([Parameter(Mandatory=$true)][string]$SdkRoot)

    $candidates = @(
        (Join-Path $SdkRoot "cmdline-tools\latest\bin\sdkmanager.bat"),
        (Join-Path $SdkRoot "cmdline-tools\latest\bin\sdkmanager.exe"),
        (Join-Path $SdkRoot "tools\bin\sdkmanager.bat"),
        (Join-Path $SdkRoot "tools\bin\sdkmanager.exe")
    )

    $cmdlineToolsRoot = Join-Path $SdkRoot "cmdline-tools"
    if (Test-Path -LiteralPath $cmdlineToolsRoot -PathType Container) {
        $toolVersions = @(Get-ChildItem -LiteralPath $cmdlineToolsRoot -Directory |
            Where-Object { $_.Name -ne "latest" } |
            Sort-Object {
                try { [version]$_.Name } catch { [version]"0.0" }
            } -Descending)

        foreach ($toolVersion in $toolVersions) {
            $candidates += (Join-Path $toolVersion.FullName "bin\sdkmanager.bat")
            $candidates += (Join-Path $toolVersion.FullName "bin\sdkmanager.exe")
        }
    }

    foreach ($candidate in $candidates) {
        if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Leaf)) {
            return (Resolve-Path -LiteralPath $candidate).Path
        }
    }

    $pathCommand = Get-Command "sdkmanager" -ErrorAction SilentlyContinue
    if ($pathCommand) {
        return $pathCommand.Source
    }

    return $null
}

function Get-LatestAvailableAndroidSdkPackageVersion {
    param(
        [Parameter(Mandatory=$true)][string]$SdkRoot,
        [Parameter(Mandatory=$true)][string]$SdkManagerCommand,
        [Parameter(Mandatory=$true)][string]$PackageName
    )

    $output = @(& $SdkManagerCommand "--sdk_root=$SdkRoot" "--list" 2>&1)
    $exitCode = $LASTEXITCODE
    if ($exitCode -ne 0) {
        throw "sdkmanager --list failed while checking $PackageName packages: $($output -join [Environment]::NewLine)"
    }

    $versions = @()
    $pattern = "^\s*$([regex]::Escape($PackageName));([0-9]+(?:\.[0-9]+)*)\s*\|"
    foreach ($line in $output) {
        $text = $line.ToString()
        if ($text -match $pattern) {
            $versions += $Matches[1]
        }
    }

    if ($versions.Count -eq 0) {
        return $null
    }

    return @($versions | Sort-Object { [version]$_ } -Descending | Select-Object -First 1)[0]
}

function Install-AndroidSdkPackage {
    param(
        [Parameter(Mandatory=$true)][string]$SdkRoot,
        [Parameter(Mandatory=$true)][string]$SdkManagerCommand,
        [Parameter(Mandatory=$true)][string]$PackageId
    )

    $licenseInput = ("y`n" * 100)
    $output = @($licenseInput | & $SdkManagerCommand "--sdk_root=$SdkRoot" "--install" $PackageId 2>&1)
    $exitCode = $LASTEXITCODE
    if ($exitCode -ne 0) {
        throw "sdkmanager failed to install $PackageId with exit code ${exitCode}: $($output -join [Environment]::NewLine)"
    }
}

function Ensure-LatestAndroidSdkPackageDirectory {
    param(
        [Parameter(Mandatory=$true)][string]$SdkRoot,
        [Parameter(Mandatory=$true)][string]$PackageName,
        [string]$SdkManagerCommand
    )

    $installedBefore = $null
    try {
        $installedBefore = Get-LatestAndroidSdkPackageDirectory -SdkRoot $SdkRoot -PackageName $PackageName
    } catch {
        $installedBefore = $null
    }

    $availableVersion = $null
    $action = "using-installed"
    if ($SdkManagerCommand) {
        $availableVersion = Get-LatestAvailableAndroidSdkPackageVersion `
            -SdkRoot $SdkRoot `
            -SdkManagerCommand $SdkManagerCommand `
            -PackageName $PackageName

        $installedVersion = if ($installedBefore) { [version]$installedBefore.Name } else { $null }
        $availableParsed = if ($availableVersion) { [version]$availableVersion } else { $null }
        if ($availableParsed -and (!$installedVersion -or $availableParsed -gt $installedVersion)) {
            Install-AndroidSdkPackage `
                -SdkRoot $SdkRoot `
                -SdkManagerCommand $SdkManagerCommand `
                -PackageId "$PackageName;$availableVersion"
            $action = "installed-latest-available"
        } elseif ($availableParsed) {
            $action = "already-latest-available"
        } else {
            $action = "available-version-not-listed"
        }
    } elseif (!$installedBefore) {
        throw "No installed Android SDK package versions found under $(Join-Path $SdkRoot $PackageName), and sdkmanager was not found."
    }

    $selected = Get-LatestAndroidSdkPackageDirectory -SdkRoot $SdkRoot -PackageName $PackageName
    return [ordered]@{
        directory = $selected
        update = [ordered]@{
            package = $PackageName
            installedVersionBefore = if ($installedBefore) { $installedBefore.Name } else { $null }
            availableVersion = $availableVersion
            selectedVersion = $selected.Name
            action = $action
        }
    }
}

function Set-AndroidSdkToolchainEnvironment {
    $sdkRoot = Get-AndroidSdkRoot
    $sdkManagerCommand = Get-AndroidSdkManagerCommand -SdkRoot $sdkRoot
    $ndkSelection = Ensure-LatestAndroidSdkPackageDirectory `
        -SdkRoot $sdkRoot `
        -PackageName "ndk" `
        -SdkManagerCommand $sdkManagerCommand
    $cmakeSelection = Ensure-LatestAndroidSdkPackageDirectory `
        -SdkRoot $sdkRoot `
        -PackageName "cmake" `
        -SdkManagerCommand $sdkManagerCommand

    $ndk = $ndkSelection.directory
    $cmake = $cmakeSelection.directory
    $cmakeCommand = Join-Path $cmake.FullName "bin\cmake.exe"

    if (!(Test-Path -LiteralPath $cmakeCommand -PathType Leaf)) {
        throw "Android SDK CMake executable not found: $cmakeCommand"
    }

    $env:ANDROID_HOME = $sdkRoot
    $env:ANDROID_SDK_ROOT = $sdkRoot
    $env:ANDROID_NDK_HOME = $ndk.FullName
    $env:ANDROID_NDK_ROOT = $ndk.FullName

    return [ordered]@{
        sdkRoot = $sdkRoot
        sdkManagerCommand = $sdkManagerCommand
        packageUpdates = @($ndkSelection.update, $cmakeSelection.update)
        ndkVersion = $ndk.Name
        ndkPath = $ndk.FullName
        cmakeVersion = $cmake.Name
        cmakeCommand = $cmakeCommand
    }
}
