param(
    [string]$ProjectRoot = (Resolve-Path (Join-Path $PSScriptRoot ".." )).Path,
    [string]$EnvFile = ".env",
    [string]$SampleImageUrl = "https://files.skinbase.org/img/aa/bb/cc/md.webp",
    [switch]$SkipAnalyze
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Write-Info([string]$Message) {
    Write-Host "[INFO] $Message" -ForegroundColor Cyan
}

function Write-Ok([string]$Message) {
    Write-Host "[OK]   $Message" -ForegroundColor Green
}

function Write-Fail([string]$Message) {
    Write-Host "[FAIL] $Message" -ForegroundColor Red
}

function Get-EnvMap([string]$Root, [string]$RelativeEnvFile) {
    $map = @{}

    $envPath = Join-Path $Root $RelativeEnvFile
    if (-not (Test-Path $envPath)) {
        $fallback = Join-Path $Root ".env.example"
        if (Test-Path $fallback) {
            Write-Info "Env file '$RelativeEnvFile' not found. Falling back to .env.example."
            $envPath = $fallback
        } else {
            throw "Neither '$RelativeEnvFile' nor '.env.example' was found in $Root"
        }
    }

    Get-Content -Path $envPath | ForEach-Object {
        $line = $_.Trim()
        if ($line -eq "" -or $line.StartsWith("#")) { return }
        $idx = $line.IndexOf("=")
        if ($idx -lt 1) { return }

        $key = $line.Substring(0, $idx).Trim()
        $val = $line.Substring($idx + 1).Trim()

        if ($val.StartsWith('"') -and $val.EndsWith('"') -and $val.Length -ge 2) {
            $val = $val.Substring(1, $val.Length - 2)
        }

        if (-not $map.ContainsKey($key)) {
            $map[$key] = $val
        }
    }

    return $map
}

function Get-Setting([hashtable]$Map, [string]$Key, [string]$Default = "") {
    $fromProcess = [Environment]::GetEnvironmentVariable($Key)
    if (-not [string]::IsNullOrWhiteSpace($fromProcess)) {
        return $fromProcess.Trim()
    }

    if ($Map.ContainsKey($Key)) {
        return [string]$Map[$Key]
    }

    return $Default
}

function Test-Truthy([string]$Value, [bool]$Default = $false) {
    if ([string]::IsNullOrWhiteSpace($Value)) { return $Default }

    switch ($Value.Trim().ToLowerInvariant()) {
        "1" { return $true }
        "true" { return $true }
        "yes" { return $true }
        "on" { return $true }
        "0" { return $false }
        "false" { return $false }
        "no" { return $false }
        "off" { return $false }
        default { return $Default }
    }
}

function Join-Url([string]$Base, [string]$Path) {
    $left = $Base.TrimEnd('/')
    $right = $Path.TrimStart('/')
    return "$left/$right"
}

function Invoke-Health([string]$Name, [string]$BaseUrl) {
    if ([string]::IsNullOrWhiteSpace($BaseUrl)) {
        throw "$Name base URL is empty"
    }

    $url = Join-Url $BaseUrl "/health"
    Write-Info "Checking $Name health: $url"

    try {
        $response = Invoke-WebRequest -Uri $url -Method GET -TimeoutSec 10 -UseBasicParsing
    } catch {
        throw "$Name health request failed: $($_.Exception.Message)"
    }

    if ($response.StatusCode -lt 200 -or $response.StatusCode -ge 300) {
        throw "$Name health returned status $($response.StatusCode)"
    }

    Write-Ok "$Name health check passed"
}

function Invoke-Analyze([string]$ClipBaseUrl, [string]$AnalyzeEndpoint, [string]$ImageUrl) {
    if ([string]::IsNullOrWhiteSpace($ClipBaseUrl)) {
        throw "CLIP base URL is empty"
    }

    $url = Join-Url $ClipBaseUrl $AnalyzeEndpoint
    Write-Info "Running sample CLIP analyze call: $url"

    $payload = @{
        image_url = $ImageUrl
    } | ConvertTo-Json -Depth 4

    try {
        $response = Invoke-WebRequest -Uri $url -Method POST -ContentType "application/json" -Body $payload -TimeoutSec 15 -UseBasicParsing
    } catch {
        throw "CLIP analyze request failed: $($_.Exception.Message)"
    }

    if ($response.StatusCode -lt 200 -or $response.StatusCode -ge 300) {
        throw "CLIP analyze returned status $($response.StatusCode)"
    }

    $json = $null
    try {
        $json = $response.Content | ConvertFrom-Json
    } catch {
        throw "CLIP analyze response is not valid JSON"
    }

    $hasTags = $false
    if ($json -is [System.Collections.IEnumerable] -and -not ($json -is [string])) {
        $hasTags = $true
    }
    if ($null -ne $json.tags) { $hasTags = $true }
    if ($null -ne $json.data) { $hasTags = $true }

    if (-not $hasTags) {
        throw "CLIP analyze response does not contain expected tags/data payload"
    }

    Write-Ok "Sample CLIP analyze call passed"
}

function Test-NonBlockingPublish([string]$Root) {
    Write-Info "Validating non-blocking publish path (code assertions)"

    $uploadController = Join-Path $Root "app/Http/Controllers/Api/UploadController.php"
    $derivativesJob = Join-Path $Root "app/Jobs/GenerateDerivativesJob.php"

    if (-not (Test-Path $uploadController)) { throw "Missing file: $uploadController" }
    if (-not (Test-Path $derivativesJob)) { throw "Missing file: $derivativesJob" }

    $uploadText = Get-Content -Raw -Path $uploadController
    $jobText = Get-Content -Raw -Path $derivativesJob

    if ($uploadText -notmatch "AutoTagArtworkJob::dispatch\(") {
        throw "UploadController does not dispatch AutoTagArtworkJob"
    }

    if ($jobText -notmatch "AutoTagArtworkJob::dispatch\(") {
        throw "GenerateDerivativesJob does not dispatch AutoTagArtworkJob"
    }

    if ($uploadText -match "dispatchSync\(" -or $jobText -match "dispatchSync\(") {
        throw "Found dispatchSync in publish path; auto-tagging must remain async"
    }

    if ($uploadText -match "Illuminate\\Support\\Facades\\Http" -or $uploadText -match "Http::") {
        throw "UploadController appears to call external vision HTTP directly"
    }

    Write-Ok "Non-blocking publish path validation passed"
}

$failed = $false

try {
    Write-Info "Vision smoke test starting"
    Write-Info "Project root: $ProjectRoot"

    $envMap = Get-EnvMap -Root $ProjectRoot -RelativeEnvFile $EnvFile

    $visionEnabled = Test-Truthy (Get-Setting -Map $envMap -Key "VISION_ENABLED" -Default "true") $true
    if (-not $visionEnabled) {
        throw "VISION_ENABLED=false. Vision integration is disabled; smoke check cannot continue."
    }

    $clipBaseUrl = Get-Setting -Map $envMap -Key "CLIP_BASE_URL"
    $clipAnalyzeEndpoint = Get-Setting -Map $envMap -Key "CLIP_ANALYZE_ENDPOINT" -Default "/analyze"

    $yoloEnabled = Test-Truthy (Get-Setting -Map $envMap -Key "YOLO_ENABLED" -Default "true") $true
    $yoloBaseUrl = Get-Setting -Map $envMap -Key "YOLO_BASE_URL"

    Invoke-Health -Name "CLIP" -BaseUrl $clipBaseUrl

    if ($yoloEnabled) {
        if ([string]::IsNullOrWhiteSpace($yoloBaseUrl)) {
            Write-Info "YOLO is enabled but YOLO_BASE_URL is empty; skipping YOLO /health check."
        } else {
            Invoke-Health -Name "YOLO" -BaseUrl $yoloBaseUrl
        }
    } else {
        Write-Info "YOLO is disabled; skipping YOLO /health check."
    }

    if ($SkipAnalyze) {
        Write-Info "Skipping sample analyze call (SkipAnalyze set)."
    } else {
        Invoke-Analyze -ClipBaseUrl $clipBaseUrl -AnalyzeEndpoint $clipAnalyzeEndpoint -ImageUrl $SampleImageUrl
    }

    Test-NonBlockingPublish -Root $ProjectRoot

    Write-Ok "Vision smoke test completed successfully"
} catch {
    $failed = $true
    Write-Fail $_.Exception.Message
}

if ($failed) {
    exit 1
}

exit 0
