param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$Url,

    [string]$OutDir = ".\output",

    [string]$Hostname = "crawler.prototype.prolicht.digital",

    [int]$MaxImages = 5,

    [int]$TimeoutSec = 300,

    [switch]$Recursive,

    [int]$Limit = 0,

    [int]$MaxDepth = 0,

    [int]$Threads = 4
)

$ErrorActionPreference = "Stop"
$hostBase = if ($Hostname.StartsWith("http")) { $Hostname } else { "https://$Hostname" }

# ---------------------------------------------------------------------------
# shared image download logic
# ---------------------------------------------------------------------------
function Download-Page {
    param($PageUrl, $MarkdownUrl, $DomainDir)

    $imagesDir = Join-Path $DomainDir "images"
    New-Item -ItemType Directory -Force -Path $imagesDir | Out-Null

    Write-Host "  dl: $PageUrl"

    $mdContent = (Invoke-WebRequest -Uri $MarkdownUrl -Method Get -TimeoutSec $TimeoutSec -MaximumRedirection 5 -ErrorAction Stop).Content

    $imageUrls = [regex]::Matches($mdContent, '!\[[^\]]*\]\((https://[^)]+)\)') | ForEach-Object { $_.Groups[1].Value }
    $imageUrls += [regex]::Matches($mdContent, 'src="(https://[^"]+/l/[^"]+)"') | ForEach-Object { $_.Groups[1].Value }
    $imageUrls = $imageUrls | Sort-Object -Unique

    $downloaded = @{}
    foreach ($imgUrl in $imageUrls) {
        if (-not $imgUrl) { continue }

        $tempFile = [System.IO.Path]::GetTempFileName()
        try {
            $imgResponse = Invoke-WebRequest -Uri $imgUrl -Method Get -TimeoutSec 30 -MaximumRedirection 5 -OutFile $tempFile -PassThru -ErrorAction Stop
        } catch {
            Remove-Item -LiteralPath $tempFile -Force -ErrorAction SilentlyContinue
            Write-Warning "    img failed: $_"
            continue
        }

        try {
            $dispMatch = if ($imgResponse.Headers["Content-Disposition"]) { [regex]::Match($imgResponse.Headers["Content-Disposition"], 'filename="?([^"]+)"?') } else { $null }
            $filename = if ($dispMatch -and $dispMatch.Success) { $dispMatch.Groups[1].Value } else { "img_$(Get-Random -Minimum 1000 -Maximum 9999)" }
            $ctMatch = if ($imgResponse.Headers["Content-Type"]) { [regex]::Match($imgResponse.Headers["Content-Type"], 'image/(\w+)') } else { $null }
            if ($ctMatch -and $ctMatch.Success) {
                $ext = $ctMatch.Groups[1].Value
                if ($filename -notmatch "\.$ext$") { $filename = "$filename.$ext" }
            }
            $localPath = Join-Path $imagesDir $filename
            Move-Item -LiteralPath $tempFile -Destination $localPath -Force -ErrorAction Stop
            $downloaded[$imgUrl] = "images/$filename"
            Write-Host "    img: $filename"
        } catch {
            Remove-Item -LiteralPath $tempFile -Force -ErrorAction SilentlyContinue
            Write-Warning "    save failed: $_"
        }
    }

    # rewrite to local paths
    foreach ($url in $downloaded.Keys) {
        $mdContent = $mdContent -replace ([regex]::Escape($url)), $downloaded[$url]
    }

    return @{ Content = $mdContent; Downloaded = $downloaded }
}

# ---------------------------------------------------------------------------
# single page crawl
# ---------------------------------------------------------------------------
function Invoke-SingleCrawl {
    Write-Host "Crawling (single): $Url"

    $body = @{ url = $Url; markdown = $true; images = $true; max_images = $MaxImages } | ConvertTo-Json
    $result = Invoke-RestMethod -Uri "$hostBase/crawl/single" -Method Post -ContentType "application/json" -Body $body -TimeoutSec $TimeoutSec

    if ($result.status -ne "ok") {
        Write-Error "Crawl failed: status=$($result.status)"
        return $false
    }

    $domain = $result.domain
    $markdownUrl = $result.markdown_url
    if (-not $markdownUrl) {
        Write-Error "No markdown_url in response"
        return $false
    }

    Write-Host "  status: ok  domain: $domain  url: $markdownUrl"

    $domainDir = Join-Path $OutDir $domain
    $page = Download-Page -PageUrl $Url -MarkdownUrl $markdownUrl -DomainDir $domainDir

    $stem = ($result.fast_markdown_path -replace ".*\\|.*/", "") -replace "\.md$", ""
    $mdPath = Join-Path $domainDir "$stem.md"
    [System.IO.File]::WriteAllText($mdPath, $page.Content, [System.Text.Encoding]::UTF8)
    Write-Host "  saved: $mdPath  images: $($page.Downloaded.Count)"

    return $true
}

# ---------------------------------------------------------------------------
# sitemap / recursive crawl
# ---------------------------------------------------------------------------
function Invoke-SitemapCrawl {
    Write-Host "Crawling (sitemap): $Url"
    Write-Host "  limit=$Limit  max-depth=$MaxDepth  threads=$Threads"

    $body = @{
        url = $Url; markdown = $true; images = $true; max_images = $MaxImages
        limit = $Limit; max_depth = $MaxDepth; recursive = $true; threads = $Threads
    } | ConvertTo-Json

    $result = Invoke-RestMethod -Uri "$hostBase/crawl/sitemap" -Method Post -ContentType "application/json" -Body $body -TimeoutSec $TimeoutSec

    Write-Host "  status: $($result.status)  total: $($result.total)  ok: $($result.succeeded)  failed: $($result.failed)"

    if ($result.failed_urls -and $result.failed_urls.Count -gt 0) {
        Write-Host "  failed URLs:"
        foreach ($u in $result.failed_urls) { Write-Host "    $u" }
    }

    $markdownUrls = $result.markdown_urls
    if (-not $markdownUrls) {
        Write-Error "No markdown_urls in response"
        return $false
    }

    $total = 0
    $ok = 0
    $fail = 0

    foreach ($pageUrl in $markdownUrls.Keys) {
        $mdl = $markdownUrls[$pageUrl]
        $total++
        if (-not $mdl) {
            Write-Warning "  no markdown URL for: $pageUrl"
            $fail++
            continue
        }

        $domain = [System.Uri]::new($pageUrl).Host
        $domainDir = Join-Path $OutDir $domain
        $page = Download-Page -PageUrl $pageUrl -MarkdownUrl $mdl -DomainDir $domainDir

        $stem = ([System.Uri]::new($pageUrl).AbsolutePath.Trim('/') -replace '[\\/:*?"<>|]', '_')
        if ($stem -eq "" -or $stem -eq "index.html") { $stem = "index" }
        $stem = $stem -replace "\.html$", ""
        $mdPath = Join-Path $domainDir "$stem.md"
        [System.IO.File]::WriteAllText($mdPath, $page.Content, [System.Text.Encoding]::UTF8)
        $ok++
        Write-Host "  [$ok/$total] saved: $mdPath  images: $($page.Downloaded.Count)"
    }

    Write-Host "`nDone: $ok pages, $fail errors"
    return ($fail -eq 0)
}

# ---------------------------------------------------------------------------
# main
# ---------------------------------------------------------------------------
if ($Recursive) {
    $success = Invoke-SitemapCrawl
} else {
    $success = Invoke-SingleCrawl
}

if (-not $success) { exit 1 }
