#!/usr/bin/env pwsh
# quality.ps1 — Local quality gate for GDScript projects (Windows).
# Usage: pwsh ./tools/quality.ps1 [-Changed] [--changed]
param([switch]$Changed)
if ($args -contains '--changed') { $Changed = $true }

$ROOT = Split-Path -Parent $PSScriptRoot
Set-Location $ROOT

$LOG = "logs/quality/latest"
New-Item -ItemType Directory -Path $LOG -Force | Out-Null

$OVERALL = $false
$ERRORS = @()
$FIXES = @()

# -- tool detection -----------------------------------------------------------
function Find-Godot {
  if ($env:GODOT_BIN) { return $env:GODOT_BIN }
  try { return (Get-Command godot -ErrorAction Stop).Source } catch {}
  try { return (Get-Command godot4 -ErrorAction Stop).Source } catch {}
  $common = @(
    "$env:ProgramFiles\Godot\godot.exe",
    "${env:ProgramFiles(x86)}\Godot\godot.exe",
    "$env:LOCALAPPDATA\Godot\godot.exe",
    "$env:USERPROFILE\AppData\Local\Godot\godot.exe"
  )
  foreach ($p in $common) { if (Test-Path $p) { return $p } }
  return $null
}

function Test-GdFormat {
  if (Get-Command gdformat -ErrorAction SilentlyContinue) { return $true }
  try {
    $null = & python -m gdtoolkit.formatter --help 2>&1
    if ($LASTEXITCODE -eq 0) { return $true }
  } catch {}
  try {
    $null = & python3 -m gdtoolkit.formatter --help 2>&1
    if ($LASTEXITCODE -eq 0) { return $true }
  } catch {}
  return $false
}

function Test-GdLint {
  if (Get-Command gdlint -ErrorAction SilentlyContinue) { return $true }
  try {
    $null = & python -m gdtoolkit.linter --help 2>&1
    if ($LASTEXITCODE -eq 0) { return $true }
  } catch {}
  try {
    $null = & python3 -m gdtoolkit.linter --help 2>&1
    if ($LASTEXITCODE -eq 0) { return $true }
  } catch {}
  return $false
}

function Invoke-GdFormat {
  $args = $args
  if (Get-Command gdformat -ErrorAction SilentlyContinue) {
    & gdformat @args 2>&1
  } else {
    & python -m gdtoolkit.formatter @args 2>&1
  }
}

function Invoke-GdLint {
  $args = $args
  if (Get-Command gdlint -ErrorAction SilentlyContinue) {
    & gdlint @args 2>&1
  } else {
    & python -m gdtoolkit.linter @args 2>&1
  }
}

function Get-ChangedGd {
  $f = git diff --name-only HEAD -- '*.gd' 2>$null
  return @($f | Where-Object { $_ -ne '' })
}

function To-ResPath($p) {
  $p = $p.Replace('\', '/')
  if ($p.StartsWith($ROOT)) { $p = $p.Substring($ROOT.Length + 1) }
  if ($p.StartsWith('./')) { $p = $p.Substring(2) }
  return "res://$p"
}

function Invoke-GodotWithTimeout {
  param([string]$Args)
  $psi = New-Object Diagnostics.ProcessStartInfo
  $psi.FileName = $GODOT
  $psi.Arguments = $Args
  $psi.RedirectStandardOutput = $true
  $psi.RedirectStandardError = $true
  $psi.UseShellExecute = $false
  $psi.CreateNoWindow = $true
  $p = [Diagnostics.Process]::Start($psi)
  $exited = $p.WaitForExit(30000)
  if (-not $exited) {
    $p.Kill()
    $output = $p.StandardOutput.ReadToEnd()
    $err = $p.StandardError.ReadToEnd()
    return $output + $err, $null
  }
  $output = $p.StandardOutput.ReadToEnd()
  $err = $p.StandardError.ReadToEnd()
  return ($output + $err), $p.ExitCode
}

# -- godot --------------------------------------------------------------------
$GODOT = Find-Godot
if (-not $GODOT) {
  Write-Host "ERROR: Godot binary not found. Set GODOT_BIN or add godot/godot4 to PATH."
  exit 1
}

# -- 1. gdformat --------------------------------------------------------------
$fmtAvail = Test-GdFormat
$fmtResult = "PASS"
Set-Content -Path "$LOG/gdformat.log" -Value "" -Encoding utf8
if (-not $fmtAvail) {
  $fmtResult = "SKIPPED"
  Add-Content -Path "$LOG/gdformat.log" -Value "gdformat not found -- install: pip install gdtoolkit" -Encoding utf8
} else {
  if ($Changed) {
    $files = Get-ChangedGd
    if ($files.Count -eq 0) {
      Add-Content -Path "$LOG/gdformat.log" -Value "No changed .gd files to check." -Encoding utf8
    } else {
      $total = 0; $bad = 0
      foreach ($f in $files) {
        if (-not (Test-Path $f)) { continue }
        $total++
        $out = Invoke-GdFormat "--check" $f
        $out | Add-Content -Path "$LOG/gdformat.log" -Encoding utf8
        if ($LASTEXITCODE -ne 0) { $bad++ }
      }
      $msg = "($bad/$total files need formatting)"
      Add-Content -Path "$LOG/gdformat.log" -Value $msg -Encoding utf8
    }
  } else {
    $out = Invoke-GdFormat "--check" "."
    $out | Add-Content -Path "$LOG/gdformat.log" -Encoding utf8
  }
}

if (Select-String -SimpleMatch "would reformat" -Path "$LOG/gdformat.log" -Quiet) {
  $fmtResult = "FAIL"
  $lines = Select-String -SimpleMatch "would reformat" -Path "$LOG/gdformat.log" | Select-Object -ExpandProperty Line
  foreach ($line in $lines) {
    $p = ($line -replace '^would reformat ', '').Trim()
    $rp = To-ResPath $p
    $ERRORS += "$rp needs formatting"
  }
  $FIXES += "Run gdformat to auto-format files"
}

# -- 2. gdlint ----------------------------------------------------------------
$lintAvail = Test-GdLint
$lintResult = "PASS"
Set-Content -Path "$LOG/gdlint.log" -Value "" -Encoding utf8
if (-not $lintAvail) {
  $lintResult = "SKIPPED"
  Add-Content -Path "$LOG/gdlint.log" -Value "gdlint not found -- install: pip install gdtoolkit" -Encoding utf8
} else {
  if ($Changed) {
    $files = Get-ChangedGd
    if ($files.Count -eq 0) {
      Add-Content -Path "$LOG/gdlint.log" -Value "No changed .gd files to lint." -Encoding utf8
    } else {
      foreach ($f in $files) {
        if (-not (Test-Path $f)) { continue }
        $out = Invoke-GdLint $f
        $out | Add-Content -Path "$LOG/gdlint.log" -Encoding utf8
      }
    }
  } else {
    $out = Invoke-GdLint "."
    $out | Add-Content -Path "$LOG/gdlint.log" -Encoding utf8
  }
}

$lintLines = Select-String -Pattern '^[^ ]+:\d+:\d+:' -Path "$LOG/gdlint.log" | Select-Object -ExpandProperty Line
if ($lintLines) {
  $lintResult = "FAIL"
  foreach ($line in $lintLines) {
    $parts = $line -split ':', 4
    if ($parts.Count -lt 4) { continue }
    $file = $parts[0]; $lineno = $parts[1]; $msg = $parts[3]
    $rp = To-ResPath $file
    $ERRORS += "$rp`:$lineno $msg"
  }
  foreach ($line in $lintLines) {
    $parts = $line -split ':', 5
    if ($parts.Count -lt 5) { continue }
    $rule = $parts[3]; $fname = Split-Path -Leaf $parts[0]
    $FIXES += "Fix $rule in $fname"
  }
}

# -- 3. godot headless check --------------------------------------------------
$godotResult = "PASS"
Set-Content -Path "$LOG/godot-check.log" -Value "" -Encoding utf8
$godotArgs = "--headless --path `"$ROOT`" --quit"
$godotOutput, $godotExit = Invoke-GodotWithTimeout -Args $godotArgs
$godotOutput | Out-File -FilePath "$LOG/godot-check.log" -Encoding utf8
if ($godotExit -eq $null) {
  $godotResult = "PASS"
  Add-Content -Path "$LOG/godot-check.log" -Value "[timed out after 30s - project runs continuously]" -Encoding utf8
} elseif ($godotExit -eq 0) {
  $godotResult = "PASS"
} else {
  $godotResult = "FAIL"
  $godotOutput | Select-String -Pattern 'error|warning|parse|syntax' | Select-Object -First 20 | ForEach-Object {
    $ERRORS += "godot: $_"
  }
  $FIXES += "Fix Godot parser errors"
}

# -- 4. GUT (optional) --------------------------------------------------------
$gutResult = "SKIPPED"
Set-Content -Path "$LOG/gut.log" -Value "" -Encoding utf8
if (Test-Path "addons/gut/gut_cmdln.gd") {
  $gutArgs = "--headless --path `"$ROOT`" -s addons/gut/gut_cmdln.gd --quit"
  $gutOutput, $gutExit = Invoke-GodotWithTimeout -Args $gutArgs
  $gutOutput | Out-File -FilePath "$LOG/gut.log" -Encoding utf8
  if ($gutExit -eq $null) {
    $gutResult = "FAIL"
    Add-Content -Path "$LOG/gut.log" -Value "[timed out after 30s]" -Encoding utf8
    $ERRORS += "gut: GUT test timed out after 30 seconds"
    $FIXES += "Fix GUT tests (timeout)"
  } elseif ($gutExit -eq 0) {
    $gutResult = "PASS"
  } else {
    $gutResult = "FAIL"
    $gutOutput | Select-String -Pattern 'fail|error|assert' | Select-Object -First 20 | ForEach-Object {
      $ERRORS += "gut: $_"
    }
    $FIXES += "Fix failing GUT tests"
  }
}

# -- summary ------------------------------------------------------------------
if ($fmtResult -eq 'FAIL' -or $lintResult -eq 'FAIL' -or $godotResult -eq 'FAIL' -or $gutResult -eq 'FAIL') {
  $OVERALL = $true
}

Write-Host ""
if ($OVERALL) { Write-Host "QUALITY RESULT: FAIL" } else { Write-Host "QUALITY RESULT: PASS" }
Write-Host ""
Write-Host "  gdformat     $fmtResult"
Write-Host "  gdlint       $lintResult"
Write-Host "  godot-check  $godotResult"
Write-Host "  gut          $gutResult"

$seen = @{}
foreach ($e in $ERRORS) {
  if ($seen.ContainsKey($e)) { continue }
  $seen[$e] = $true
  Write-Host "    $e"
}

if ($OVERALL -and $FIXES.Count -gt 0) {
  Write-Host ""
  Write-Host "  NEXT FIX:"
  $seenFix = @{}
  $idx = 1
  foreach ($fix in $FIXES) {
    if ($seenFix.ContainsKey($fix)) { continue }
    $seenFix[$fix] = $true
    Write-Host "    $idx. $fix"
    $idx++
  }
}

Write-Host ""
Write-Host "Full logs:"
Write-Host "  $LOG/"
Write-Host ""

# save summary
if ($OVERALL) { $resultText = "FAIL" } else { $resultText = "PASS" }
$summary = @"
QUALITY RESULT: $resultText

  gdformat     $fmtResult
  gdlint       $lintResult
  godot-check  $godotResult
  gut          $gutResult
"@
Set-Content -Path "$LOG/summary.txt" -Value $summary -Encoding utf8

if ($OVERALL) { exit 1 } else { exit 0 }
