param(
    [string]$JavaHome,
    [string]$GradleExe,
    [string]$SdkDir,
    [string]$KeystoreInfoPath,
    [string]$RepositoryUrl,
    [string]$HomepageUrl,
    [string]$ReleaseNotes,
    [switch]$SkipBuild
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Read-KeyValueFile {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    if (-not (Test-Path -LiteralPath $Path)) {
        throw "File not found: $Path"
    }

    $result = @{}
    foreach ($line in Get-Content -LiteralPath $Path) {
        if ([string]::IsNullOrWhiteSpace($line) -or $line.TrimStart().StartsWith("#")) {
            continue
        }

        $parts = $line -split "=", 2
        if ($parts.Count -eq 2) {
            $result[$parts[0].Trim()] = $parts[1].Trim()
        }
    }

    return $result
}

function Resolve-JavaHome {
    param([string]$PreferredJavaHome)

    $candidates = @()
    if ($PreferredJavaHome) {
        $candidates += $PreferredJavaHome
    }
    if ($env:JAVA_HOME) {
        $candidates += $env:JAVA_HOME
    }
    $candidates += @(
        "C:\Program Files\Android\Android Studio\jbr",
        "C:\Program Files\Android\openjdk"
    )

    foreach ($candidate in $candidates | Select-Object -Unique) {
        if ($candidate -and (Test-Path -LiteralPath (Join-Path $candidate "bin\java.exe"))) {
            return $candidate
        }
    }

    throw "Java runtime not found. Pass -JavaHome explicitly."
}

function Resolve-GradleExe {
    param([string]$PreferredGradleExe)

    $candidates = @()
    if ($PreferredGradleExe) {
        $candidates += $PreferredGradleExe
    }
    $wrapperRoot = Join-Path $env:USERPROFILE ".gradle\wrapper\dists"
    $preferredGradle = Join-Path $wrapperRoot "gradle-8.13-bin"
    if (Test-Path -LiteralPath $preferredGradle) {
        $candidates += Get-ChildItem -LiteralPath $preferredGradle -Recurse -Filter "gradle.bat" -ErrorAction SilentlyContinue |
            Select-Object -ExpandProperty FullName
    }
    if (Test-Path -LiteralPath $wrapperRoot) {
        $candidates += Get-ChildItem -LiteralPath $wrapperRoot -Recurse -Filter "gradle.bat" -ErrorAction SilentlyContinue |
            Sort-Object FullName -Descending |
            Select-Object -ExpandProperty FullName
    }

    foreach ($candidate in $candidates | Select-Object -Unique) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) {
            return $candidate
        }
    }

    throw "Gradle executable not found. Pass -GradleExe explicitly."
}

function Resolve-SdkDir {
    param(
        [string]$PreferredSdkDir,
        [string]$ProjectRoot
    )

    $candidates = @()
    if ($PreferredSdkDir) {
        $candidates += $PreferredSdkDir
    }

    $localPropertiesPath = Join-Path $ProjectRoot "local.properties"
    if (Test-Path -LiteralPath $localPropertiesPath) {
        $localProperties = Read-KeyValueFile -Path $localPropertiesPath
        if ($localProperties.ContainsKey("sdk.dir")) {
            $candidates += ($localProperties["sdk.dir"] -replace "\\\\", "\")
        }
    }

    if ($env:ANDROID_SDK_ROOT) {
        $candidates += $env:ANDROID_SDK_ROOT
    }
    if ($env:ANDROID_HOME) {
        $candidates += $env:ANDROID_HOME
    }

    foreach ($candidate in $candidates | Select-Object -Unique) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) {
            return $candidate
        }
    }

    throw "Android SDK not found. Pass -SdkDir explicitly."
}

function Get-LatestBuildToolsDir {
    param(
        [Parameter(Mandatory = $true)]
        [string]$SdkDir
    )

    $buildToolsRoot = Join-Path $SdkDir "build-tools"
    if (-not (Test-Path -LiteralPath $buildToolsRoot)) {
        throw "Android build-tools directory not found: $buildToolsRoot"
    }

    $latest = Get-ChildItem -LiteralPath $buildToolsRoot -Directory |
        Sort-Object { [version]$_.Name } -Descending |
        Select-Object -First 1

    if (-not $latest) {
        throw "No Android build-tools versions found in $buildToolsRoot"
    }

    return $latest.FullName
}

function Get-ArgusTemplate {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectRoot
    )

    $searchRoot = Join-Path $ProjectRoot "artifacts\argus"
    if (-not (Test-Path -LiteralPath $searchRoot)) {
        return $null
    }

    $templatePath = Get-ChildItem -LiteralPath $searchRoot -Recurse -Filter "argus-release.json" -File |
        Sort-Object FullName -Descending |
        Select-Object -First 1 -ExpandProperty FullName

    if (-not $templatePath) {
        return $null
    }

    return (Get-Content -LiteralPath $templatePath -Raw | ConvertFrom-Json)
}

function Resolve-PackageFileName {
    param(
        [string]$Version,
        [long]$VersionCode
    )

    return "aletheia-$Version-$VersionCode.apk"
}

function Resolve-KeystoreInfoPath {
    param(
        [string]$PreferredPath,
        [string]$ProjectRoot
    )

    $candidates = @()
    if ($PreferredPath) {
        $candidates += $PreferredPath
    }
    $candidates += @(
        (Join-Path $ProjectRoot "signing\release-keystore-info.txt"),
        (Join-Path $env:USERPROFILE ".aletheia\signing\release-keystore-info.txt")
    )

    foreach ($candidate in $candidates | Select-Object -Unique) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) {
            return $candidate
        }
    }

    throw "release-keystore-info.txt not found. Pass -KeystoreInfoPath explicitly."
}

function Resolve-KeystorePath {
    param(
        [string]$ConfiguredPath,
        [string]$KeystoreInfoPath,
        [string]$ProjectRoot
    )

    $candidates = @()
    if ($ConfiguredPath) {
        $candidates += $ConfiguredPath

        $configuredLeaf = Split-Path -Leaf $ConfiguredPath
        if ($configuredLeaf) {
            $candidates += (Join-Path (Split-Path -Parent $KeystoreInfoPath) $configuredLeaf)
            $candidates += (Join-Path (Join-Path $ProjectRoot "signing") $configuredLeaf)
        }
    }

    foreach ($candidate in $candidates | Select-Object -Unique) {
        if ($candidate -and (Test-Path -LiteralPath $candidate)) {
            return $candidate
        }
    }

    throw "Keystore file not found. Checked: $($candidates -join ', ')"
}

function Resolve-GitOriginUrl {
    param([string]$ProjectRoot)

    $gitConfigPath = Join-Path $ProjectRoot ".git\config"
    if (-not (Test-Path -LiteralPath $gitConfigPath)) {
        return $null
    }

    $inOriginSection = $false
    foreach ($line in Get-Content -LiteralPath $gitConfigPath) {
        if ($line -match '^\s*\[remote\s+"origin"\]\s*$') {
            $inOriginSection = $true
            continue
        }
        if ($line -match '^\s*\[') {
            $inOriginSection = $false
        }
        if ($inOriginSection -and $line -match '^\s*url\s*=\s*(.+?)\s*$') {
            return $matches[1]
        }
    }

    return $null
}

$projectRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
$javaHomePath = Resolve-JavaHome -PreferredJavaHome $JavaHome
$gradleExePath = Resolve-GradleExe -PreferredGradleExe $GradleExe
$sdkDirPath = Resolve-SdkDir -PreferredSdkDir $SdkDir -ProjectRoot $projectRoot
$keystoreInfoPath = Resolve-KeystoreInfoPath -PreferredPath $KeystoreInfoPath -ProjectRoot $projectRoot
$buildToolsDir = Get-LatestBuildToolsDir -SdkDir $sdkDirPath
$gitOriginUrl = Resolve-GitOriginUrl -ProjectRoot $projectRoot
$zipalignExe = Join-Path $buildToolsDir "zipalign.exe"
$apksignerBat = Join-Path $buildToolsDir "apksigner.bat"

if (-not (Test-Path -LiteralPath $zipalignExe)) {
    throw "zipalign.exe not found: $zipalignExe"
}
if (-not (Test-Path -LiteralPath $apksignerBat)) {
    throw "apksigner.bat not found: $apksignerBat"
}

$env:JAVA_HOME = $javaHomePath
$env:ANDROID_SDK_ROOT = $sdkDirPath

if (-not $SkipBuild) {
    & $gradleExePath assembleRelease --stacktrace
}

$outputMetadataPath = Join-Path $projectRoot "app\build\outputs\apk\release\output-metadata.json"
if (-not (Test-Path -LiteralPath $outputMetadataPath)) {
    throw "Release output metadata not found: $outputMetadataPath"
}

$outputMetadata = Get-Content -LiteralPath $outputMetadataPath -Raw | ConvertFrom-Json
$releaseElement = $outputMetadata.elements | Select-Object -First 1
if (-not $releaseElement) {
    throw "Release output metadata does not contain any elements."
}

$versionName = [string]$releaseElement.versionName
if ([string]::IsNullOrWhiteSpace($versionName)) {
    throw "versionName is empty in release output metadata."
}
$versionCode = [long]$releaseElement.versionCode
if ($versionCode -le 0) {
    throw "versionCode must be positive in release output metadata."
}

$unsignedApk = Join-Path $projectRoot ("app\build\outputs\apk\release\" + $releaseElement.outputFile)
if (-not (Test-Path -LiteralPath $unsignedApk)) {
    throw "Unsigned release APK not found: $unsignedApk"
}

$keystoreInfo = Read-KeyValueFile -Path $keystoreInfoPath
foreach ($requiredKey in @("KeystorePath", "Alias", "StorePassword", "KeyPassword", "StoreType")) {
    if (-not $keystoreInfo.ContainsKey($requiredKey)) {
        throw "Missing '$requiredKey' in $keystoreInfoPath"
    }
}
$keystorePath = Resolve-KeystorePath -ConfiguredPath $keystoreInfo["KeystorePath"] -KeystoreInfoPath $keystoreInfoPath -ProjectRoot $projectRoot

$template = Get-ArgusTemplate -ProjectRoot $projectRoot
$slug = if ($template -and $template.slug) { [string]$template.slug } else { "aletheia-kotlin" }
$packageFile = Resolve-PackageFileName -Version $versionName -VersionCode $versionCode
$outputDir = Join-Path $projectRoot ("artifacts\argus\$slug\$versionName")
$alignedApk = Join-Path $outputDir ([IO.Path]::GetFileNameWithoutExtension($packageFile) + "-aligned.apk")
$signedApk = Join-Path $outputDir $packageFile
$idsigPath = $signedApk + ".idsig"
$summaryPath = Join-Path $outputDir "argus-publish-summary.txt"
$manifestPath = Join-Path $outputDir "argus-release.json"

New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
foreach ($path in @($alignedApk, $signedApk, $idsigPath)) {
    if (Test-Path -LiteralPath $path) {
        Remove-Item -LiteralPath $path -Force
    }
}

& $zipalignExe -p -f 4 $unsignedApk $alignedApk

$storePassFile = Join-Path $env:TEMP "aletheia-store-pass.txt"
$keyPassFile = Join-Path $env:TEMP "aletheia-key-pass.txt"

try {
    Set-Content -LiteralPath $storePassFile -Value $keystoreInfo["StorePassword"] -NoNewline
    Set-Content -LiteralPath $keyPassFile -Value $keystoreInfo["KeyPassword"] -NoNewline

    & $apksignerBat sign `
        --ks $keystorePath `
        --ks-type $keystoreInfo["StoreType"] `
        --ks-key-alias $keystoreInfo["Alias"] `
        --ks-pass ("file:" + $storePassFile) `
        --key-pass ("file:" + $keyPassFile) `
        --v4-signing-enabled true `
        --out $signedApk `
        $alignedApk
}
finally {
    foreach ($secretPath in @($storePassFile, $keyPassFile)) {
        if (Test-Path -LiteralPath $secretPath) {
            Remove-Item -LiteralPath $secretPath -Force
        }
    }
    if (Test-Path -LiteralPath $alignedApk) {
        Remove-Item -LiteralPath $alignedApk -Force
    }
}

$verifyOutputLines = & $apksignerBat verify --verbose --print-certs --v4-signature-file $idsigPath $signedApk
$verifyOutput = $verifyOutputLines | Out-String
foreach ($requiredVerificationLine in @(
    "Verified using v2 scheme (APK Signature Scheme v2): true",
    "Verified using v3 scheme (APK Signature Scheme v3): true",
    "Verified using v4 scheme (APK Signature Scheme v4): true"
)) {
    if ($verifyOutput -notmatch [regex]::Escape($requiredVerificationLine)) {
        throw "APK verification did not confirm expected signature state: $requiredVerificationLine"
    }
}

$signerInfoLines = $verifyOutputLines | Where-Object { $_ -like "Signer #1 certificate *" }
if (-not $signerInfoLines) {
    throw "Could not extract signer certificate details from apksigner output."
}

$signerInfo = $signerInfoLines | Out-String
$packageSha256 = ((Get-FileHash -LiteralPath $signedApk -Algorithm SHA256).Hash).ToLowerInvariant()

$releaseNotesText = if ($PSBoundParameters.ContainsKey("ReleaseNotes")) {
    $ReleaseNotes
}
else {
    "Technical release $versionName for Aletheia Kotlin: project version bumped and Argus package prepared."
}

$repositoryUrlValue = if ($PSBoundParameters.ContainsKey("RepositoryUrl")) {
    $RepositoryUrl
}
elseif ($gitOriginUrl) {
    $gitOriginUrl
}
else {
    ""
}

$homepageUrlValue = if ($PSBoundParameters.ContainsKey("HomepageUrl")) {
    $HomepageUrl
}
else {
    ""
}

$manifest = [ordered]@{
    slug = $slug
    name = if ($template -and $template.name) { $template.name } else { "Aletheia" }
    summary = if ($template -and $template.summary) { $template.summary } else { "Android reader app." }
    description = if ($template -and $template.description) { $template.description } else { "Android application package prepared for Argus." }
    repositoryUrl = $repositoryUrlValue
    homepageUrl = $homepageUrlValue
    version = $versionName
    channel = if ($template -and $template.channel) { $template.channel } else { "stable" }
    platform = if ($template -and $template.platform) { $template.platform } else { "android" }
    packageKind = if ($template -and $template.packageKind) { $template.packageKind } else { "apk" }
    releaseNotes = $releaseNotesText
    # The updater reads the public manifest endpoint. An unlisted app returns 404 there,
    # so Aletheia releases must stay visible to the public Argus API.
    publicCatalog = $true
    packageFile = $packageFile
    packageSha256 = $packageSha256
    signerInfo = ($signerInfo.TrimEnd() -replace "\r?\n", [Environment]::NewLine)
}

$manifestJson = $manifest | ConvertTo-Json -Depth 4
Set-Content -LiteralPath $manifestPath -Value $manifestJson -Encoding utf8

$summaryLines = @(
    "Slug: $($manifest.slug)",
    "Name: $($manifest.name)",
    "Summary: $($manifest.summary)",
    "Description: $($manifest.description)",
    "Repository URL: $($manifest.repositoryUrl)",
    "Homepage URL: $($manifest.homepageUrl)",
    "Version: $($manifest.version)",
    "Channel: $($manifest.channel)",
    "Platform: $($manifest.platform)",
    "Package kind: $($manifest.packageKind)",
    "Release notes: $($manifest.releaseNotes)",
    "Package file: $signedApk",
    "Show in public catalog: $($manifest.publicCatalog)",
    "Package SHA-256: $($manifest.packageSha256)",
    "",
    "Signer:",
    $manifest.signerInfo
)

Set-Content -LiteralPath $summaryPath -Value $summaryLines -Encoding utf8

Write-Host "Argus package prepared:" $signedApk
Write-Host "SHA-256:" $packageSha256
Write-Host "Manifest:" $manifestPath
Write-Host "Summary:" $summaryPath
