param (
    [string]$BackendUrl = "https://bdma.co.in/earnapp",
    [int]$VMId = 0,
    [string]$Group = "Default",
    [string]$UnitId = "DefaultUnit",
    [string]$RdpNickname = "UnknownRDP"
)

[System.Net.ServicePointManager]::DnsRefreshTimeout = 0
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes

$LogPath    = "$env:APPDATA\SinnerWatchdog\agent.log"
$ConfigPath = "$env:APPDATA\SinnerWatchdog\config.json"

$HeartbeatInterval   = 60
$StatsSyncInterval   = 300
$CommandPollInterval = 10
$LogCleanupInterval = 1800
$lastLogCleanup = Get-Date


function Write-LocalLog {
    param($msg)
    $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    if (!(Test-Path (Split-Path $LogPath))) {
        New-Item -ItemType Directory -Path (Split-Path $LogPath) -Force | Out-Null
    }
    "[$ts] $msg" | Out-File $LogPath -Append -Encoding utf8
}

function Get-PublicIP {
    try { (Invoke-RestMethod "https://api.ipify.org?format=json" -TimeoutSec 5).ip }
    catch {
        try { (Invoke-RestMethod "http://ip-api.com/json" -TimeoutSec 5).query }
        catch { "0.0.0.0" }
    }
}

function Save-Config {
    param($Id)
    @{ vm_id = $Id } | ConvertTo-Json | Out-File $ConfigPath -Force
}

function Load-Config {
    if (Test-Path $ConfigPath) {
        (Get-Content $ConfigPath | ConvertFrom-Json).vm_id
    } else { $null }
}

function Get-EarnAppStatus {
    param($elements)

    foreach ($e in $elements) {
        $t = $e.Current.Name

        if ($t -like "*Earning income*") {
            return "Running"
        }

        if ($t -like "*Blocked*") {
            return "Blocked"
        }

        if ($t -like "*resource sharing paused*") {
            return "Paused"
        }

        if ($t -like "*Connecting*") {
            return "Connecting"
        }

        if ($t -like "*Resume earning*") {
            return "Paused"
        }
    }

    return $null
}

function Get-EarnAppStats {

    Write-LocalLog "EarnApp stats started"

    $stats = @{ balance = 0; traffic = "0B"; status = "Offline" }

    function Find-Window {
        try {
            $ae   = [System.Windows.Automation.AutomationElement]::RootElement
            $cond = New-Object System.Windows.Automation.PropertyCondition(
                [System.Windows.Automation.AutomationElement]::NameProperty, "EarnApp"
            )
            return $ae.FindFirst([System.Windows.Automation.TreeScope]::Children, $cond)
        } catch { return $null }
    }

    $win = Find-Window

    if (-not $win) {
        Write-LocalLog "EarnApp not found. Waiting 2 minutes before restart"
        Start-Sleep 100
        $win = Find-Window

        if (-not $win) {
            Write-LocalLog "Still not found. Restarting EarnApp"
            Stop-Process -Name EarnApp -Force -ErrorAction SilentlyContinue
            Start-Sleep 2
            try { Start-Process "C:\Program Files (x86)\EarnApp\EarnApp.exe" } catch {}
            Start-Sleep 40
            $win = Find-Window
        }
    }

    try {
        if ($win) {

            $els = $win.FindAll(
                [System.Windows.Automation.TreeScope]::Descendants,
                [System.Windows.Automation.Condition]::TrueCondition
            )

            foreach ($e in $els) {
                $t = $e.Current.Name
                if ($t -match "\$") {
                    $parsed = [decimal]($t -replace '[^0-9\.]', '')
                    if ($parsed -gt 0) {
                        $stats.balance = $parsed
                    }
                }
            }

            $detectedStatus = Get-EarnAppStatus -elements $els

            if ($detectedStatus) {
                $stats.status = $detectedStatus
            } else {
                $stats.status = "Offline"
            }

        } else {
            $stats.status = "Offline"
        }
    } catch {
        $stats.status = "Offline"
        Write-LocalLog "EarnApp stats error: $($_.Exception.Message)"
    }

    Write-LocalLog "EarnApp stats finished | Balance=$($stats.balance) | Status=$($stats.status)"
    return $stats
}

function Get-EarnAppToken {
    try {
        $path = "$env:LOCALAPPDATA\BrightData"
        if (Test-Path $path) {
            $file = Get-ChildItem $path -File | Select-Object -First 1
            if ($file) {
                $c = (Get-Content $file.FullName -Raw).Trim()
                if ($c.Contains(":")) { ($c -split ":")[0].Trim() } else { $c }
            } else { "Not Found" }
        } else { "Not Found" }
    } catch { "Error" }
}

function Ensure-EarnAppReady {

    Write-LocalLog "Ensure-EarnAppReady started"

    Stop-Process -Name EarnApp -Force -ErrorAction SilentlyContinue
    Start-Sleep 2

    try {
        Start-Process "C:\Program Files (x86)\EarnApp\EarnApp.exe"
    } catch {
        Write-LocalLog "Failed to start EarnApp"
    }

    Start-Sleep 15

    function Find-Window {
        try {
            $ae   = [System.Windows.Automation.AutomationElement]::RootElement
            $cond = New-Object System.Windows.Automation.PropertyCondition(
                [System.Windows.Automation.AutomationElement]::NameProperty, "EarnApp"
            )
            return $ae.FindFirst([System.Windows.Automation.TreeScope]::Children, $cond)
        } catch { return $null }
    }

    $win = Find-Window

    if ($win) {
        Write-LocalLog "EarnApp window ready"
        return $true
    } else {
        Write-LocalLog "EarnApp window not found after restart"
        return $false
    }
}

function Invoke-RestMethodWithRetry {
    param(
        [string]$Uri,
        [string]$Method = "GET",
        [string]$ContentType = "application/json",
        [string]$Body = "",
        [int]$TimeoutSec = 15,
        [int]$MaxRetries = 3,
        [int]$InitialDelaySec = 5
    )

    for ($i = 0; $i -lt $MaxRetries; $i++) {
        try {
            if ($Method -eq "GET") {
                return Invoke-RestMethod -Uri $Uri -Method GET -TimeoutSec $TimeoutSec -DisableKeepAlive
            } else {
                return Invoke-RestMethod -Uri $Uri -Method $Method -ContentType $ContentType -Body $Body -TimeoutSec $TimeoutSec -DisableKeepAlive
            }
        } catch {
            Write-LocalLog "Invoke-RestMethod failed (Attempt $($i+1)/$MaxRetries): $($_.Exception.Message)"
            if ($i -lt ($MaxRetries - 1)) {
                $delay = $InitialDelaySec * [Math]::Pow(2, $i)
                Write-LocalLog "Retrying in $delay seconds..."
                Start-Sleep $delay
            } else {
                Write-LocalLog "Max retries reached. Failing request."
                throw $_.Exception
            }
        }
    }
}

function Send-Data {
    param($id, $stats)
    try {
        Invoke-RestMethodWithRetry "$BackendUrl/save_data.php" -Method POST -ContentType "application/json" -TimeoutSec 15 `
        -Body (@{
            vm_id         = $id
            unit_id       = $UnitId
            group_name    = $Group
            rdp_nickname  = $RdpNickname
            balance       = ("{0:F4}" -f [decimal]$stats.balance)
            traffic       = $stats.traffic
            status        = $stats.status
            ip_address    = (Get-PublicIP)
            earnapp_token = (Get-EarnAppToken)
        } | ConvertTo-Json)
    } catch {
        Write-LocalLog "Sync Error: $($_.Exception.Message)"
    }
}

$CurrentVmId = if ($VMId -ne 0) { $VMId } else { Load-Config }

if (-not $CurrentVmId) {
    while (-not $CurrentVmId) {
        try {
            $r = Invoke-RestMethodWithRetry "$BackendUrl/api/vm/register.php" -Method POST -ContentType "application/json" -TimeoutSec 15 `
            -Body (@{
                group_name   = $Group
                rdp_nickname = $RdpNickname
                unit_id      = $UnitId
                assigned_ip  = (Get-PublicIP)
            } | ConvertTo-Json)
            $CurrentVmId = $r.vm_id
            Save-Config $CurrentVmId
        } catch {
            Write-LocalLog "VM registration failed: $($_.Exception.Message)"
            Start-Sleep 10
        }
    }
}

$lastHeartbeat   = Get-Date 0
$lastStatsSync   = Get-Date 0
$lastCommandPoll = Get-Date 0
 
Write-LocalLog "Initial stats sync on startup"
$stats = Get-EarnAppStats
Send-Data $CurrentVmId $stats

while ($true) {

    Start-Sleep 5
    $now = Get-Date

    if (($now - $lastCommandPoll).TotalSeconds -ge $CommandPollInterval) {
        try {
            $cmd = Invoke-RestMethodWithRetry "$BackendUrl/pages/api.php?action=fetch_command&vm_id=$CurrentVmId" -TimeoutSec 10

            if ($cmd -and $cmd.action) {

                Write-LocalLog "Command received: $($cmd.action)"

                if ($cmd.action -eq "restart_vm") {
                    Write-LocalLog "VM restart command received"
                    Invoke-RestMethodWithRetry "$BackendUrl/pages/api.php?action=ack_command&id=$($cmd.id)&vm_id=$CurrentVmId"
                    Start-Process "shutdown.exe" -ArgumentList "/r /t 5 /f" -WindowStyle Hidden
                }

                elseif ($cmd.action -eq "fetch_balance") {
                    Write-LocalLog "Fetch balance command received"
                    if (Ensure-EarnAppReady) {
                        Start-Sleep 20
                        $stats = Get-EarnAppStats
                        Send-Data $CurrentVmId $stats
                    }
                    Invoke-RestMethodWithRetry "$BackendUrl/pages/api.php?action=command_result" -Method POST -ContentType "application/json" `
                    -Body (@{
                        command_id = $cmd.id
                        success    = $true
                        message    = "Balance fetched after restart"
                    } | ConvertTo-Json)
                    Invoke-RestMethodWithRetry "$BackendUrl/pages/api.php?action=ack_command&id=$($cmd.id)&vm_id=$CurrentVmId"
                }

                elseif ($cmd.action -eq "fetch_balance_global") {
                    Write-LocalLog "Group balance command received"
                    if (Ensure-EarnAppReady) {
                        $stats = Get-EarnAppStats
                        Send-Data $CurrentVmId $stats
                    }
                    Invoke-RestMethodWithRetry "$BackendUrl/pages/api.php?action=ack_command&id=$($cmd.id)&vm_id=$CurrentVmId"
                }
            }
        } catch {
            Write-LocalLog "Command poll failed: $($_.Exception.Message)"
        }
        $lastCommandPoll = $now
    }

    if (($now - $lastHeartbeat).TotalSeconds -ge $HeartbeatInterval) {
        try {
            Invoke-RestMethodWithRetry "$BackendUrl/save_data.php" -Method POST -ContentType "application/json" `
            -Body (@{
                action     = "heartbeat"
                vm_id      = $CurrentVmId
                ip_address = (Get-PublicIP)
            } | ConvertTo-Json)
            Write-LocalLog "Heartbeat sent - VM $CurrentVmId"
        } catch {
            Write-LocalLog "Heartbeat failed: $($_.Exception.Message)"
        }
        $lastHeartbeat = $now
    }

    if (($now - $lastStatsSync).TotalSeconds -ge $StatsSyncInterval) {
        $stats = Get-EarnAppStats
        Send-Data $CurrentVmId $stats
        $lastStatsSync = $now
    }
    
    if (($now - $lastLogCleanup).TotalSeconds -ge $LogCleanupInterval) {
        try {
            if (Test-Path $LogPath) {
                Clear-Content $LogPath
                Write-LocalLog "Local log cleared automatically"
            }
        } catch {}
        $lastLogCleanup = $now
    }
}