# Full API test using /login and database menu queries
$companyUrl = "http://localhost:8006"

# Step 1: Login via /login (which is in SecurityConfig anonymous whitelist)
Write-Host "=== Step 1: Login ==="
$loginBody = '{"tenantCode":"T202605253515","username":"admin","password":"admin123"}'
$loginResp = Invoke-WebRequest -Uri "$companyUrl/login" -Method POST -ContentType "application/json" -Body $loginBody -UseBasicParsing
$loginJson = $loginResp.Content | ConvertFrom-Json
$token = $loginJson.token
Write-Host "Token OK, length: $($token.Length)"

$headers = @{ Authorization = "Bearer $token" }

# Step 2: Get menu routes via getRouters
Write-Host "`n=== Step 2: Get Routers ==="
$routesResp = Invoke-WebRequest -Uri "$companyUrl/getRouters" -Method GET -Headers $headers -UseBasicParsing
$routesJson = $routesResp.Content | ConvertFrom-Json
Write-Host "getRouters code: $($routesJson.code)"

# Recursive extract perms from menu tree
function Extract-Perms {
    param($menuList)
    $results = @()
    foreach ($menu in $menuList) {
        if ($menu.perms -and $menu.perms.Trim() -ne "") {
            $results += $menu.perms
        }
        if ($menu.children -and $menu.children.Count -gt 0) {
            $results += Extract-Perms -menuList $menu.children
        }
    }
    return $results
}

$allPerms = Extract-Perms -menuList $routesJson.data
Write-Host "Perms from getRouters: $($allPerms.Count)"

# Step 3: Also get menu list from system/menu/list (has more detail including buttons)
Write-Host "`n=== Step 3: Get Menu List ==="
try {
    $menuResp = Invoke-WebRequest -Uri "$companyUrl/system/menu/list" -Method GET -Headers $headers -UseBasicParsing
    $menuJson = $menuResp.Content | ConvertFrom-Json
    Write-Host "system/menu/list code: $($menuJson.code), total: $($menuJson.total)"
    
    # Extract perms from menu list
    $menuPerms = @()
    foreach ($row in $menuJson.rows) {
        if ($row.perms -and $row.perms.Trim() -ne "") {
            $menuPerms += $row.perms
        }
    }
    Write-Host "Perms from system/menu/list: $($menuPerms.Count)"
} catch {
    Write-Host "system/menu/list error: $($_.Exception.Message)"
    $menuPerms = @()
}

# Merge all perms
$allPerms = $allPerms + $menuPerms | Sort-Object -Unique
Write-Host "`nTotal unique perms: $($allPerms.Count)"

# Step 4: Convert perms to API paths
# perms format: module:entity:action -> /module/entity/action
$apiPaths = @{}
foreach ($perm in $allPerms) {
    if ($perm -match "^\s*$") { continue }
    $parts = $perm -split ":"
    if ($parts.Count -ge 3) {
        $mod = $parts[0]
        $ent = $parts[1]
        $act = $parts[2]
        $p = "/$mod/$ent/$act"
        if (-not $apiPaths.ContainsKey($p)) { $apiPaths[$p] = $perm }
    } elseif ($parts.Count -eq 2) {
        $mod = $parts[0]
        $ent = $parts[1]
        $p = "/$mod/$ent/list"
        if (-not $apiPaths.ContainsKey($p)) { $apiPaths[$p] = $perm }
    }
}

Write-Host "Total unique API paths from perms: $($apiPaths.Count)"

# Step 5: Also extract component paths from menus (page routes)
Write-Host "`n=== Step 5: Extract page routes ==="
$pagePaths = @{}
function Extract-PageRoutes {
    param($menuList)
    foreach ($menu in $menuList) {
        if ($menu.path -and $menu.path -ne "" -and $menu.component -and $menu.component -ne "") {
            $pagePaths[$menu.path] = $menu.component
        }
        if ($menu.children -and $menu.children.Count -gt 0) {
            Extract-PageRoutes -menuList $menu.children
        }
    }
}
Extract-PageRoutes -menuList $routesJson.data
Write-Host "Page routes: $($pagePaths.Count)"

# Step 6: Now scan saasadminui src/api for ALL API URLs
Write-Host "`n=== Step 6: Scan saasadminui src/api for URLs ==="
$apiFiles = Get-ChildItem -Path "d:\ylrz\saasadminui\src\api" -Recurse -Filter "*.js"
$frontendApis = @{}
foreach ($file in $apiFiles) {
    $content = Get-Content $file.FullName -Raw -Encoding UTF8
    # Match url patterns: url: '/xxx/yyy/zzz' or url: "/xxx/yyy/zzz"
    $matches = [regex]::Matches($content, "url:\s*['`"]([^'`"]+)['`"]")
    foreach ($m in $matches) {
        $url = $m.Groups[1].Value
        # Remove template variables like ${xxx}
        $cleanUrl = $url -replace '\$\{[^}]+\}', '1'
        if (-not $frontendApis.ContainsKey($cleanUrl)) {
            $frontendApis[$cleanUrl] = $file.Name
        }
    }
}
Write-Host "Frontend API URLs found: $($frontendApis.Count)"

# Step 7: Test ALL unique API paths (from perms + frontend)
Write-Host "`n=== Step 7: Test all API endpoints ==="

# Merge all paths
$testPaths = @{}
foreach ($p in $apiPaths.Keys) { if (-not $testPaths.ContainsKey($p)) { $testPaths[$p] = "perms:$($apiPaths[$p])" } }
foreach ($p in $frontendApis.Keys) { 
    if (-not $testPaths.ContainsKey($p)) { $testPaths[$p] = "frontend:$($frontendApis[$p])" }
}

Write-Host "Total unique paths to test: $($testPaths.Count)"

$ok = [System.Collections.ArrayList]::new()
$notFound = [System.Collections.ArrayList]::new()
$serverErr = [System.Collections.ArrayList]::new()
$forbidden = [System.Collections.ArrayList]::new()
$timeout = [System.Collections.ArrayList]::new()
$other = [System.Collections.ArrayList]::new()

$i = 0
$total = $testPaths.Count
foreach ($path in ($testPaths.Keys | Sort-Object)) {
    $i++
    $source = $testPaths[$path]
    if ($i % 100 -eq 0) { Write-Host "  Progress: $i / $total" }
    
    # Determine method - GET for list/query, POST for add/edit/remove
    $method = "GET"
    if ($path -match "/add|/edit|/remove|/delete|/save|/update|/import|/export|/upload") {
        $method = "POST"
    }
    
    try {
        if ($method -eq "GET") {
            $resp = Invoke-WebRequest -Uri "$companyUrl$path" -Method GET -Headers $headers -UseBasicParsing -TimeoutSec 8
        } else {
            $resp = Invoke-WebRequest -Uri "$companyUrl$path" -Method POST -Headers $headers -ContentType "application/json" -Body "{}" -UseBasicParsing -TimeoutSec 8
        }
        $body = $resp.Content | ConvertFrom-Json
        if ($body.code -and $body.code -ne 200) {
            $code = "$($body.code)"
            if ($code -eq "404") { [void]$notFound.Add("$path | $source") }
            elseif ($code -eq "500") { [void]$serverErr.Add("$path | $source") }
            elseif ($code -eq "403") { [void]$forbidden.Add("$path | $source") }
            else { [void]$other.Add("$path | $source | code=$code") }
        } else {
            [void]$ok.Add("$path | $source")
        }
    } catch {
        $err = "$($_.Exception.Message)"
        if ($err -match "404") { [void]$notFound.Add("$path | $source") }
        elseif ($err -match "500") { [void]$serverErr.Add("$path | $source") }
        elseif ($err -match "403") { [void]$forbidden.Add("$path | $source") }
        elseif ($err -match "timeout|timed out") { [void]$timeout.Add("$path | $source") }
        else { [void]$other.Add("$path | $source | err") }
    }
}

# Step 8: Summary
Write-Host "`n========================================"
Write-Host "=== API Test Results ==="
Write-Host "========================================"
Write-Host "200 OK: $($ok.Count)"
Write-Host "404 Not Found: $($notFound.Count)"
Write-Host "500 Server Error: $($serverErr.Count)"
Write-Host "403 Forbidden: $($forbidden.Count)"
Write-Host "TIMEOUT: $($timeout.Count)"
Write-Host "OTHER: $($other.Count)"

if ($notFound.Count -gt 0) {
    Write-Host "`n--- 404 Details ---"
    $notFound | Sort-Object | ForEach-Object { Write-Host "  $_" }
}

if ($serverErr.Count -gt 0) {
    Write-Host "`n--- 500 Details ---"
    $serverErr | Sort-Object | ForEach-Object { Write-Host "  $_" }
}

if ($forbidden.Count -gt 0) {
    Write-Host "`n--- 403 Details ---"
    $forbidden | Sort-Object | ForEach-Object { Write-Host "  $_" }
}

if ($timeout.Count -gt 0) {
    Write-Host "`n--- TIMEOUT Details ---"
    $timeout | Sort-Object | ForEach-Object { Write-Host "  $_" }
}

if ($other.Count -gt 0) {
    Write-Host "`n--- OTHER Details ---"
    $other | Sort-Object | ForEach-Object { Write-Host "  $_" }
}

# Save results
$out = [System.Collections.ArrayList]::new()
[void]$out.Add("API Test Results - $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')")
[void]$out.Add("=" * 60)
[void]$out.Add("200 OK: $($ok.Count)")
[void]$out.Add("404 Not Found: $($notFound.Count)")
[void]$out.Add("500 Server Error: $($serverErr.Count)")
[void]$out.Add("403 Forbidden: $($forbidden.Count)")
[void]$out.Add("TIMEOUT: $($timeout.Count)")
[void]$out.Add("OTHER: $($other.Count)")
[void]$out.Add("")
[void]$out.Add("=== 200 OK ===")
$ok | Sort-Object | ForEach-Object { [void]$out.Add($_) }
[void]$out.Add("")
[void]$out.Add("=== 404 Not Found ===")
$notFound | Sort-Object | ForEach-Object { [void]$out.Add($_) }
[void]$out.Add("")
[void]$out.Add("=== 500 Server Error ===")
$serverErr | Sort-Object | ForEach-Object { [void]$out.Add($_) }
[void]$out.Add("")
[void]$out.Add("=== 403 Forbidden ===")
$forbidden | Sort-Object | ForEach-Object { [void]$out.Add($_) }
[void]$out.Add("")
[void]$out.Add("=== TIMEOUT ===")
$timeout | Sort-Object | ForEach-Object { [void]$out.Add($_) }
[void]$out.Add("")
[void]$out.Add("=== OTHER ===")
$other | Sort-Object | ForEach-Object { [void]$out.Add($_) }

[System.IO.File]::WriteAllLines("d:\ylrz\saasadminui\api_test_results.txt", $out, (New-Object System.Text.UTF8Encoding($false)))
Write-Host "`nResults saved to d:\ylrz\saasadminui\api_test_results.txt"
