[CmdletBinding()]
param()

$ErrorActionPreference = 'Stop'
$kitRoot = Split-Path -Parent $PSScriptRoot
$adb = Join-Path $kitRoot 'platform-tools\adb.exe'
$fastboot = Join-Path $kitRoot 'platform-tools\fastboot.exe'
$stockA = Join-Path $kitRoot 'Image\stock_init_boot_a.img'
$stockB = Join-Path $kitRoot 'Image\stock_init_boot_b.img'

function ConvertTo-NativeArgument {
    param([AllowEmptyString()][string]$Value)

    if ([string]::IsNullOrEmpty($Value)) { return '""' }
    if ($Value -notmatch '[\s"]') { return $Value }

    $quoted = '"'
    $backslashes = 0
    foreach ($character in $Value.ToCharArray()) {
        if ($character -eq '\') {
            $backslashes++
            continue
        }
        if ($character -eq '"') {
            $quoted += ('\' * (($backslashes * 2) + 1)) + '"'
            $backslashes = 0
            continue
        }
        if ($backslashes -gt 0) {
            $quoted += '\' * $backslashes
            $backslashes = 0
        }
        $quoted += $character
    }
    if ($backslashes -gt 0) { $quoted += '\' * ($backslashes * 2) }
    return $quoted + '"'
}

function Get-NativeOutput {
    param(
        [Parameter(Mandatory = $true)]
        [string]$FilePath,
        [string[]]$Arguments = @(),
        [Parameter(Mandatory = $true)]
        [string]$Failure
    )

    $startInfo = [Diagnostics.ProcessStartInfo]::new()
    $startInfo.FileName = $FilePath
    $startInfo.Arguments = (($Arguments | ForEach-Object { ConvertTo-NativeArgument $_ }) -join ' ')
    $startInfo.UseShellExecute = $false
    $startInfo.CreateNoWindow = $true
    $startInfo.RedirectStandardOutput = $true
    $startInfo.RedirectStandardError = $true

    $process = [Diagnostics.Process]::new()
    $process.StartInfo = $startInfo
    if (-not $process.Start()) { throw "$Failure (process did not start)." }
    $stdoutTask = $process.StandardOutput.ReadToEndAsync()
    $stderrTask = $process.StandardError.ReadToEndAsync()
    $process.WaitForExit()
    $stdout = $stdoutTask.Result
    $stderr = $stderrTask.Result
    $exitCode = $process.ExitCode
    $process.Dispose()

    $output = @()
    if (-not [string]::IsNullOrWhiteSpace($stdout)) {
        $output += @($stdout.TrimEnd() -split "`r?`n")
    }
    if (-not [string]::IsNullOrWhiteSpace($stderr)) {
        $output += @($stderr.TrimEnd() -split "`r?`n")
    }
    if ($exitCode -ne 0) {
        if ($output.Count -gt 0) { $output | Out-Host }
        throw "$Failure (exit $exitCode)."
    }
    return $output
}

function Get-AdbOutput {
    param(
        [Parameter(Mandatory = $true)]
        [string[]]$Arguments,
        [Parameter(Mandatory = $true)]
        [string]$Failure
    )

    return @(Get-NativeOutput -FilePath $adb -Arguments $Arguments -Failure $Failure)
}

function Invoke-AdbChecked {
    param(
        [Parameter(Mandatory = $true)]
        [string[]]$Arguments,
        [Parameter(Mandatory = $true)]
        [string]$Failure
    )

    $output = @(Get-AdbOutput -Arguments $Arguments -Failure $Failure)
    if ($output.Count -gt 0) { $output | Out-Host }
}

Write-Host 'NEXON X11 stock init_boot rollback' -ForegroundColor Yellow
Write-Host 'Connect USB A-to-A to the rear USB port. This removes TECHNOZON apps and restores the active stock init_boot partition.'
Write-Host 'WARNING: the clean NEXON image contains an unmanaged stock su. Removing Magisk reduces root access control.' -ForegroundColor Red
if ((Read-Host 'Type STOCK to continue') -ne 'STOCK') { exit 2 }

$deviceList = @(Get-AdbOutput -Arguments @('devices') -Failure 'Cannot query ADB devices')
$serials = @($deviceList | Select-Object -Skip 1 | ForEach-Object {
    if ($_ -match '^(\S+)\s+device$') { $matches[1] }
})
$usbSerials = @($serials | Where-Object { $_ -notmatch ':' })
if ($usbSerials.Count -ne 1) {
    throw "Exactly one USB ADB device must be online. USB=$($usbSerials.Count), all=$($serials.Count)"
}
$serial = $usbSerials[0]
$device = ((Get-AdbOutput -Arguments @('-s', $serial, 'shell', 'getprop', 'ro.product.device') -Failure 'Cannot read device identity') -join '').Trim()
$incremental = ((Get-AdbOutput -Arguments @('-s', $serial, 'shell', 'getprop', 'ro.build.version.incremental') -Failure 'Cannot read firmware build') -join '').Trim()
if ($device -ne 'ross' -or $incremental -ne '250926144031') {
    throw "Wrong device: device=$device incremental=$incremental"
}
$slot = ((Get-AdbOutput -Arguments @('-s', $serial, 'shell', 'getprop', 'ro.boot.slot_suffix') -Failure 'Cannot read active boot slot') -join '').Trim()
if ($slot -notin @('_a', '_b')) { throw "Unsupported or missing Android boot slot '$slot'." }
$stockPartition = "init_boot$slot"
$stock = if ($slot -eq '_a') { $stockA } else { $stockB }
Write-Host "Active Android slot=$slot; rollback partition=$stockPartition" -ForegroundColor Yellow

$cleanupRemote = '/data/local/tmp/technozon_x11_stock_cleanup.sh'
$cleanupLocal = Join-Path ([IO.Path]::GetTempPath()) ('technozon_x11_stock_cleanup_{0}.sh' -f [Guid]::NewGuid().ToString('N'))
$cleanupBody = @'
#!/system/bin/sh
set -eu

TV_MODULE=/data/adb/modules/tvsmbmanager
TV_DATA=/data/adb/tvsmbmanager

stop_tvsmb_processes() {
  for pid in $(pidof smbd nmbd 2>/dev/null || true); do
    exe=$(readlink "/proc/$pid/exe" 2>/dev/null || true)
    case "$exe" in
      "$TV_MODULE/bin/arm/smbd"|"$TV_MODULE/bin/arm/nmbd") kill -KILL "$pid" 2>/dev/null || true ;;
    esac
  done

  ps -A -o PID,ARGS 2>/dev/null | while read -r pid command arguments; do
    [ "$command" = "sh" ] || [ "$command" = "/system/bin/sh" ] || continue
    case "$arguments" in
      "$TV_MODULE/service.sh --watchdog-supervisor"|"$TV_MODULE/service.sh --watchdog-worker")
        kill -KILL "$pid" 2>/dev/null || true
        ;;
    esac
  done
}

run_uninstall() {
  module=$1
  [ ! -f "/data/adb/modules/$module/remove" ] || return 0
  if [ -x "/data/adb/modules/$module/uninstall.sh" ]; then
    sh "/data/adb/modules/$module/uninstall.sh"
  fi
}

echo "[1/6] Stopping TECHNOZON services"
stop_tvsmb_processes
echo "[2/6] Running module uninstall hooks"
run_uninstall technozon_x11_hardening
run_uninstall technozon_x11_performance
run_uninstall tvsmbmanager

echo "[3/6] Marking Magisk modules for removal"
for module in \
  technozon_x11_hardening \
  technozon_x11_light_sleep \
  technozon_x11_performance \
  tvsmbmanager \
  stesh_apps \
  stesh_bootanimation \
  stesh_legacy_su_guard \
  technozon_atv_adblock \
  technozon_tv_channel
do
  mkdir -p "/data/adb/modules/$module"
  touch "/data/adb/modules/$module/remove"
done

echo "[4/6] Removing TECHNOZON applications"
for package in \
  info.technozon.steshmodifier \
  ua.tvsearch.app \
  ua.com.technozon.tvwebview \
  ua.com.technozon.tvboxeswebview \
  com.topjohnwu.magisk
do
  pm uninstall --user 0 "$package" >/dev/null 2>&1 || true
done

echo "[5/6] Restoring the known NEXON X11 stock profile"
for package in com.ald.update com.ald.apkmanage com.ald.preinstall com.DeviceTest; do
  cmd package install-existing --user 0 "$package" >/dev/null 2>&1 || true
  pm enable --user 0 "$package" >/dev/null 2>&1 || true
done

settings put global transition_animation_scale 0.25
settings put global window_animation_scale 0.25
settings put global animator_duration_scale 0.25
settings put secure install_non_market_apps 1
settings put global install_non_market_apps 1
settings put global device_name 'NEXON X11'
cmd hdmi_control cec_setting set volume_control_enabled 1 >/dev/null 2>&1 || true
settings put global hdmi_control_volume_control_enabled 1
setprop persist.vendor.rtk.sd.aisr 0
setprop persist.sys.wifi.adb true
settings put global adb_enabled 1
settings put global adb_wifi_enabled 1
settings put global verifier_verify_adb_installs 0

echo "[6/6] Removing TV SMB private data"
rm -rf "$TV_DATA"
[ ! -e "$TV_DATA" ] || {
  echo "Failed to remove $TV_DATA" >&2
  exit 1
}
echo "ROLLBACK_CLEANUP_OK"
'@

try {
    [IO.File]::WriteAllText(
        $cleanupLocal,
        ($cleanupBody -replace "`r`n", "`n"),
        [Text.UTF8Encoding]::new($false)
    )
    Invoke-AdbChecked -Arguments @('-s', $serial, 'push', $cleanupLocal, $cleanupRemote) -Failure 'Failed to upload rollback cleanup script'
    Invoke-AdbChecked -Arguments @('-s', $serial, 'shell', 'chmod', '700', $cleanupRemote) -Failure 'Failed to prepare rollback cleanup script'
    & $adb -s $serial shell test -x /debug_ramdisk/su 2>$null
    if ($LASTEXITCODE -eq 0) {
        $rootCleanupArguments = @('-s', $serial, 'shell', '/debug_ramdisk/su', '-c', $cleanupRemote)
    } else {
        $rootCleanupArguments = @('-s', $serial, 'shell', 'su', '0', 'sh', $cleanupRemote)
    }
    Invoke-AdbChecked -Arguments $rootCleanupArguments -Failure 'Rollback cleanup failed; init_boot was not changed'
} finally {
    Remove-Item -LiteralPath $cleanupLocal -Force -ErrorAction SilentlyContinue
}
Invoke-AdbChecked -Arguments @('-s', $serial, 'shell', 'rm', '-f', $cleanupRemote) -Failure 'Failed to remove temporary cleanup script'

Invoke-AdbChecked -Arguments @('-s', $serial, 'reboot', 'bootloader') -Failure 'Failed to enter bootloader; init_boot was not changed'

$deadline = (Get-Date).AddSeconds(90)
do {
    $fastbootLine = (& $fastboot devices 2>$null | Select-Object -First 1)
    if ($fastbootLine -match '\s+fastboot$') { break }
    Start-Sleep -Seconds 2
} while ((Get-Date) -lt $deadline)
if ($fastbootLine -notmatch '\s+fastboot$') { throw 'Fastboot device not found.' }

$productOutput = [IO.Path]::GetTempFileName()
$productError = [IO.Path]::GetTempFileName()
try {
    $process = Start-Process -FilePath $fastboot `
        -ArgumentList @('getvar', 'product') `
        -NoNewWindow -Wait -PassThru `
        -RedirectStandardOutput $productOutput `
        -RedirectStandardError $productError
    if ($process.ExitCode -ne 0) { throw 'Cannot read fastboot product.' }
    $product = @(
        Get-Content -LiteralPath $productOutput -ErrorAction SilentlyContinue
        Get-Content -LiteralPath $productError -ErrorAction SilentlyContinue
    ) -join "`n"
} finally {
    Remove-Item -LiteralPath $productOutput -Force -ErrorAction SilentlyContinue
    Remove-Item -LiteralPath $productError -Force -ErrorAction SilentlyContinue
}
if ($product -notmatch 'product:\s*kylin') { throw "Wrong bootloader product: $product" }

$expected = '7DE9439FAD4540D39A716E32346B8B8D76D48EA6C1A9D3025E9D3A1BA02DD82A'
$actual = (Get-FileHash -LiteralPath $stock -Algorithm SHA256).Hash
if ($actual -ne $expected) { throw "$stockPartition stock image hash mismatch." }

$flashOutput = @(Get-NativeOutput -FilePath $fastboot -Arguments @('flash', $stockPartition, $stock) -Failure "$stockPartition stock flash failed. Do not disconnect power")
if ($flashOutput.Count -gt 0) { $flashOutput | Out-Host }
$rebootOutput = @(Get-NativeOutput -FilePath $fastboot -Arguments @('reboot') -Failure 'Fastboot reboot failed')
if ($rebootOutput.Count -gt 0) { $rebootOutput | Out-Host }
Write-Host "Stock $stockPartition restored. The box is rebooting." -ForegroundColor Green
