# Thorough removal of Paychek Safeguard / companion traces from NinjaTrader Custom.
# End users: double-click Uninstall-NinjaAddon.bat (close NinjaTrader first).
$ErrorActionPreference = "Continue"

$script:Deleted = New-Object System.Collections.Generic.List[string]
$script:Failed = New-Object System.Collections.Generic.List[string]
$script:Skipped = New-Object System.Collections.Generic.List[string]

function Write-Banner {
    Write-Host ""
    Write-Host "=== Paychek Safeguard - Uninstall NinjaTrader addon (thorough) ===" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "Close NinjaTrader completely BEFORE continuing (files may be locked)." -ForegroundColor Yellow
    Write-Host ""
}

function Get-NinjaCustomRoots {
    $roots = New-Object System.Collections.Generic.List[string]
    $candidates = @(
        (Join-Path $env:USERPROFILE "Documents\NinjaTrader 8\bin\Custom"),
        (Join-Path ([Environment]::GetFolderPath("MyDocuments")) "NinjaTrader 8\bin\Custom")
    )

    # Common OneDrive / relocated Documents layouts
    foreach ($leaf in @(
        "OneDrive\Documents\NinjaTrader 8\bin\Custom",
        "OneDrive\Documentos\NinjaTrader 8\bin\Custom",
        "OneDrive - Personal\Documents\NinjaTrader 8\bin\Custom"
    )) {
        $candidates += (Join-Path $env:USERPROFILE $leaf)
    }

    foreach ($c in $candidates) {
        if ([string]::IsNullOrWhiteSpace($c)) { continue }
        try {
            $full = [System.IO.Path]::GetFullPath($c)
        } catch {
            $full = $c
        }
        if ((Test-Path -LiteralPath $full) -and ($roots -notcontains $full)) {
            $roots.Add($full) | Out-Null
        }
    }
    return $roots
}

function Remove-TrackedItem {
    param(
        [Parameter(Mandatory = $true)][string]$Path,
        [switch]$Recurse
    )
    if (-not (Test-Path -LiteralPath $Path)) { return $false }
    try {
        Remove-Item -LiteralPath $Path -Force -Recurse:$Recurse -ErrorAction Stop
        $script:Deleted.Add($Path) | Out-Null
        Write-Host "  OK deleted: $Path" -ForegroundColor Green
        return $true
    } catch {
        $script:Failed.Add("$Path  ($($_.Exception.Message))") | Out-Null
        Write-Host "  FAIL: $Path - $($_.Exception.Message)" -ForegroundColor Red
        return $false
    }
}

function Test-PaychekOrSafeguardName {
    param([Parameter(Mandatory = $true)][string]$Name)
    # -like is case-insensitive; avoid regex | quoting pitfalls on Windows PowerShell 5.1
    return ($Name -like '*Paychek*') -or ($Name -like '*Safeguard*')
}

function Test-NinjaCustomAssemblyName {
    param([Parameter(Mandatory = $true)][string]$Name)
    return ($Name -like 'NinjaTrader.Custom.dll') -or
           ($Name -like 'NinjaTrader.Custom.pdb') -or
           ($Name -like 'NinjaTrader.Custom.xml')
}

function Remove-KnownAddonFiles {
    param([Parameter(Mandatory = $true)][string]$Custom)

    # Same list as Install-NinjaAddon (+ indicators / legacy stubs)
    $patterns = @(
        "AddOns\PaychekSafeguardAddOn.cs",
        "AddOns\PaychekZoneChartToolbar.cs",
        "AddOns\PaychekSafeguardZoneHelpers.cs",
        "AddOns\PaychekChartTraderCloseGuard.cs",
        "AddOns\paycheksafeguard.cs",
        "DrawingTools\PaychekSafeguardZone.cs",
        "Indicators\PaychekCumulativeVolumeDelta.cs",
        "PaychekSafeguard.json"
    )
    foreach ($rel in $patterns) {
        Remove-TrackedItem -Path (Join-Path $Custom $rel) | Out-Null
    }
}

function Remove-PaychekJsonFiles {
    param([Parameter(Mandatory = $true)][string]$Custom)
    Get-ChildItem -LiteralPath $Custom -File -Force -ErrorAction SilentlyContinue |
        Where-Object { $_.Name -like 'Paychek*.json' -or $_.Name -like '*PaychekSafeguard*.json' } |
        ForEach-Object { Remove-TrackedItem -Path $_.FullName | Out-Null }
}

function Remove-LeftoverNameMatches {
    param([Parameter(Mandatory = $true)][string]$Custom)

    # Deep scan: renamed copies / duplicates under Custom (files + dirs named Paychek/Safeguard)
    $hits = @(
        Get-ChildItem -LiteralPath $Custom -Recurse -Force -ErrorAction SilentlyContinue |
            Where-Object {
                $_.Name -and (Test-PaychekOrSafeguardName -Name $_.Name) -and
                # Never touch the main Custom assembly (shared by all NinjaScript)
                -not (Test-NinjaCustomAssemblyName -Name $_.Name)
            }
    )

    # Delete files first, then directories (deepest first)
    $files = @($hits | Where-Object { -not $_.PSIsContainer } | Sort-Object { $_.FullName.Length } -Descending)
    $dirs = @($hits | Where-Object { $_.PSIsContainer } | Sort-Object { $_.FullName.Length } -Descending)

    foreach ($f in $files) {
        $ext = [System.IO.Path]::GetExtension($f.Name).ToLowerInvariant()
        # Only delete Paychek-named compiled leftovers for binary types; still remove .cs/.json/etc. for Safeguard*
        if ($ext -in @('.dll', '.pdb', '.nupkg', '.exe')) {
            if ($f.Name -notlike '*Paychek*') {
                $script:Skipped.Add("$($f.FullName) (binary without Paychek in name)") | Out-Null
                continue
            }
        }
        Remove-TrackedItem -Path $f.FullName | Out-Null
    }
    foreach ($d in $dirs) {
        Remove-TrackedItem -Path $d.FullName -Recurse | Out-Null
    }
}

function Clear-CsprojPaychekEntries {
    param([Parameter(Mandatory = $true)][string]$Custom)
    $csproj = Join-Path $Custom "NinjaTrader.Custom.csproj"
    if (-not (Test-Path -LiteralPath $csproj)) { return }

    try {
        $lines = [System.IO.File]::ReadAllLines($csproj)
        $cleanLines = New-Object System.Collections.Generic.List[string]
        $removed = 0
        foreach ($line in $lines) {
            $isCompileInclude = ($line -like '*<Compile Include=*') -or ($line -like '*<Compile Include =*')
            $isPaychekOrSafeguard = ($line -like '*Paychek*') -or ($line -like '*Safeguard*')
            if ($isCompileInclude -and $isPaychekOrSafeguard) {
                $removed++
                continue
            }
            $cleanLines.Add($line) | Out-Null
        }
        if ($removed -gt 0) {
            $utf8NoBom = New-Object System.Text.UTF8Encoding $false
            [System.IO.File]::WriteAllLines($csproj, $cleanLines.ToArray(), $utf8NoBom)
            $script:Deleted.Add("$csproj  (cleaned Compile Include lines)") | Out-Null
            Write-Host "  OK cleaned Paychek/Safeguard entries in NinjaTrader.Custom.csproj" -ForegroundColor Green
        }
    } catch {
        $script:Failed.Add("$csproj  ($($_.Exception.Message))") | Out-Null
        Write-Host "  FAIL cleaning csproj: $($_.Exception.Message)" -ForegroundColor Red
    }
}

function Test-YesAnswer {
    param([string]$Answer)
    if ([string]::IsNullOrWhiteSpace($Answer)) { return $true }
    $t = $Answer.Trim()
    return ($t -like 'Y*')
}

function Clear-PaychekCompanion {
    param([switch]$ForceYes)

    $UserData = Join-Path $env:LOCALAPPDATA "PaychekCompanion"
    Write-Host ""
    Write-Host "Thorough clean - also remove local companion data?" -ForegroundColor Yellow
    Write-Host "  $UserData"
    Write-Host "  (zones, rules, ledger)"
    Write-Host ""

    $ans = "Y"
    if (-not $ForceYes) {
        try {
            $ans = Read-Host "Remove PaychekCompanion? [Y]/N"
        } catch {
            $ans = "Y"
        }
        if ([string]::IsNullOrWhiteSpace($ans)) { $ans = "Y" }
    }

    # Empty Enter = Yes (thorough clean). Explicit N = keep data.
    if (Test-YesAnswer -Answer $ans) {
        if (Test-Path -LiteralPath $UserData) {
            Remove-TrackedItem -Path $UserData -Recurse | Out-Null
        } else {
            Write-Host "  PaychekCompanion already absent." -ForegroundColor DarkGray
        }
    } else {
        Write-Host "  Kept PaychekCompanion data." -ForegroundColor DarkGray
    }
}

# --- main ---
Write-Banner
try {
    Read-Host "Press Enter when NinjaTrader is closed" | Out-Null
} catch { }

Get-Process -Name "NinjaTrader*" -ErrorAction SilentlyContinue | ForEach-Object {
    Write-Host "Stopping $($_.ProcessName) (PID $($_.Id))..."
    Stop-Process -Id $_.Id -Force -ErrorAction SilentlyContinue
}
Start-Sleep -Seconds 1

$customs = @(Get-NinjaCustomRoots)
if ($customs.Count -eq 0) {
    Write-Host ""
    Write-Host "[WARN] No NinjaTrader Custom folder found under Documents / OneDrive." -ForegroundColor Yellow
    Write-Host "Checked USERPROFILE\Documents and MyDocuments + OneDrive variants."
} else {
    Write-Host ""
    Write-Host "Scanning Custom root(s) for Paychek / Safeguard traces..."
    foreach ($Custom in $customs) {
        Write-Host ""
        Write-Host ">>> $Custom" -ForegroundColor Cyan
        Remove-KnownAddonFiles -Custom $Custom
        Remove-PaychekJsonFiles -Custom $Custom
        Remove-LeftoverNameMatches -Custom $Custom
        Clear-CsprojPaychekEntries -Custom $Custom
    }
}

Clear-PaychekCompanion

Write-Host ""
Write-Host "=== SUMMARY ===" -ForegroundColor Cyan
if ($script:Deleted.Count -eq 0) {
    Write-Host "  Nothing deleted (already clean, or Custom path missing)." -ForegroundColor DarkGray
} else {
    Write-Host "  Deleted ($($script:Deleted.Count)):" -ForegroundColor Green
    foreach ($p in $script:Deleted) {
        Write-Host "    - $p"
    }
}
if ($script:Failed.Count -gt 0) {
    Write-Host "  Failed ($($script:Failed.Count)) - close NT and retry:" -ForegroundColor Red
    foreach ($p in $script:Failed) {
        Write-Host "    - $p"
    }
}
if ($script:Skipped.Count -gt 0) {
    Write-Host "  Skipped binaries (no Paychek in name):" -ForegroundColor DarkGray
    foreach ($p in $script:Skipped) {
        Write-Host "    - $p"
    }
}

Write-Host ""
Write-Host "=== DONE ===" -ForegroundColor Green
Write-Host ""
Write-Host "Open NinjaTrader -> New -> NinjaScript Editor -> Compile (F5)" -ForegroundColor Cyan
Write-Host "Paychek must NOT appear under Control Center -> New"
Write-Host "If compile still lists Paychek errors, search Custom for leftover *Paychek* files."
Write-Host ""
try {
    Read-Host "Press Enter to close" | Out-Null
} catch {
    Start-Sleep -Seconds 3
}
