param(
    [int]$PollSeconds = 15,
    [string]$Implementer = "",
    [string[]]$Reviewers = @(),
    [switch]$DisableCodexMaster,
    [switch]$DisableAutoFix,
    [switch]$DryRun,
    [switch]$Once,
    [int]$MaxTasks = 0,
    [string]$InboxDirectory = ""
)

. (Join-Path $PSScriptRoot "Common.ps1")

$roots = Get-RalphTaskRoots
foreach ($path in $roots.Values) {
    Ensure-Directory -Path $path
}

if ([string]::IsNullOrWhiteSpace($InboxDirectory)) {
    $InboxDirectory = $roots.Inbox
}
Ensure-Directory -Path $InboxDirectory

$lockFile = Get-RalphStateFile -Name "inbox_daemon.lock.json"
$daemonStateFile = Get-RalphStateFile -Name "inbox_daemon_state.json"
$script:daemonRunId = "daemon-" + (Get-Date -Format "yyyyMMdd-HHmmss")
$script:processedCount = 0

function Save-DaemonState {
    param(
        [string]$Status,
        [string]$Message,
        [hashtable]$Extra = @{}
    )

    $state = [ordered]@{
        pid = $PID
        status = $Status
        message = $Message
        updated_at = (Get-Date).ToString("o")
        poll_seconds = $PollSeconds
        dry_run = [bool]$DryRun
        inbox_directory = $InboxDirectory
        processed_count = $script:processedCount
    }
    foreach ($key in $Extra.Keys) {
        $state[$key] = $Extra[$key]
    }
    Write-JsonFile -Path $daemonStateFile -Object $state
}

function Acquire-DaemonLock {
    if (Test-Path $lockFile) {
        try {
            $existing = Read-JsonFile -Path $lockFile
            $existingPid = 0
            try { $existingPid = [int]$existing.pid } catch { $existingPid = 0 }
            if ($existingPid -gt 0) {
                $proc = Get-Process -Id $existingPid -ErrorAction SilentlyContinue
                if ($null -ne $proc) {
                    throw "Ralph inbox daemon already running with PID $existingPid."
                }
            }
        }
        catch {
            # stale or malformed lock; overwrite it
        }
    }

    Write-JsonFile -Path $lockFile -Object ([ordered]@{
        pid = $PID
        started_at = (Get-Date).ToString("o")
        inbox_directory = $InboxDirectory
    })
}

function Release-DaemonLock {
    if (Test-Path $lockFile) {
        Remove-Item -LiteralPath $lockFile -Force -ErrorAction SilentlyContinue
    }
}

function Get-NextInboxItem {
    $directories = @(Get-ChildItem -LiteralPath $InboxDirectory -Directory -ErrorAction SilentlyContinue | Sort-Object LastWriteTime, Name)
    foreach ($dir in $directories) {
        if (Test-Path (Join-Path $dir.FullName "TASK.md")) {
            return [ordered]@{
                kind = "taskpack"
                path = $dir.FullName
                name = $dir.Name
            }
        }
    }

    $files = @(Get-ChildItem -LiteralPath $InboxDirectory -File -Filter *.md -ErrorAction SilentlyContinue | Sort-Object LastWriteTime, Name)
    foreach ($file in $files) {
        return [ordered]@{
            kind = "markdown"
            path = $file.FullName
            name = $file.Name
        }
    }

    return $null
}

function Move-InboxItemToProcessing {
    param([hashtable]$Item)

    $id = "{0}-{1}" -f (Get-Date -Format "yyyyMMdd-HHmmss"), (Convert-ToRalphSlug -Text ([System.IO.Path]::GetFileNameWithoutExtension($Item.name)))
    $targetDir = Join-Path $roots.Processing $id
    Ensure-Directory -Path $targetDir

    if ($Item.kind -eq "taskpack") {
        $targetParent = Split-Path -Parent $targetDir
        if (-not (Test-Path $targetParent)) {
            Ensure-Directory -Path $targetParent
        }
        Remove-Item -LiteralPath $targetDir -Recurse -Force -ErrorAction SilentlyContinue
        Move-Item -LiteralPath $Item.path -Destination $targetDir
        return [ordered]@{
            id = $id
            task_directory = $targetDir
            title = $Item.name
            source = $Item.path
        }
    }

    $taskInfo = New-RalphTaskPackFromMarkdown -MarkdownPath $Item.path -TaskId $id -TargetDirectory $targetDir
    Remove-Item -LiteralPath $Item.path -Force
    return [ordered]@{
        id = $taskInfo.id
        task_directory = $taskInfo.task_directory
        title = $taskInfo.title
        source = $Item.path
    }
}

function Finalize-ProcessedItem {
    param(
        [hashtable]$Processed,
        [string]$DestinationRoot,
        [string]$State,
        [string]$RunId = "",
        [string]$Summary = ""
    )

    Ensure-Directory -Path $DestinationRoot
    $destination = Join-Path $DestinationRoot ([System.IO.Path]::GetFileName($Processed.task_directory))
    if (Test-Path $destination) {
        Remove-Item -LiteralPath $destination -Recurse -Force -ErrorAction SilentlyContinue
    }
    Move-Item -LiteralPath $Processed.task_directory -Destination $destination

    $submissionFile = Join-Path $destination "submission.json"
    $submission = [ordered]@{
        id = $Processed.id
        title = $Processed.title
        state = $State
        finished_at = (Get-Date).ToString("o")
        source = $Processed.source
        run_id = $RunId
        summary = $Summary
    }
    Write-JsonFile -Path $submissionFile -Object $submission
    return $destination
}

Acquire-DaemonLock
Save-DaemonState -Status "running" -Message "Inbox daemon started"
Add-RalphEvent -RunId $script:daemonRunId -Stage "daemon" -Status "running" -Actor "daemon" -Message "Ralph inbox daemon started" -Data @{
    inbox_directory = $InboxDirectory
}
Send-TelegramNotification `
    -EventName "daemon_started" `
    -Title "Ralph daemon started" `
    -Message ("Inbox: " + $InboxDirectory) `
    -RunId $script:daemonRunId `
    -Stage "daemon" `
    -Status "running" | Out-Null

try {
    while ($true) {
        $item = Get-NextInboxItem
        if ($null -eq $item) {
            if ($Once) {
                Save-DaemonState -Status "idle" -Message "No tasks in inbox; exiting because -Once was used"
                break
            }
            Save-DaemonState -Status "idle" -Message "Waiting for inbox tasks"
            Start-Sleep -Seconds $PollSeconds
            continue
        }

        $processed = Move-InboxItemToProcessing -Item $item
        $script:processedCount += 1
        Save-DaemonState -Status "running" -Message ("Processing task " + $processed.id) -Extra @{
            current_task = $processed.id
            current_task_directory = $processed.task_directory
        }
        Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "started" -Actor "daemon" -Message ("Dequeued task " + $processed.id) -Data @{
            task_directory = $processed.task_directory
        }
        Send-TelegramNotification `
            -EventName "task_processing" `
            -Title "Task processing" `
            -Message ($processed.title + "`n" + $processed.task_directory) `
            -RunId $processed.id `
            -Stage "queue" `
            -Status "started" | Out-Null

        $runId = ""
        $summary = ""
        try {
            $args = @(
                "-ExecutionPolicy", "Bypass",
                "-File", (Join-Path $PSScriptRoot "Start-RalphAutopilot.ps1"),
                "-TaskDirectory", $processed.task_directory,
                "-RunLabel", "queue"
            )
            if ($DryRun) {
                $args += "-DryRun"
            }
            if (-not [string]::IsNullOrWhiteSpace($Implementer)) {
                $args += @("-Implementer", $Implementer)
            }
            foreach ($reviewer in $Reviewers) {
                $args += @("-Reviewers", $reviewer)
            }
            if ($DisableCodexMaster) {
                $args += "-UseCodexMaster:$false"
            }
            if ($DisableAutoFix) {
                $args += "-AutoFix:$false"
            }

            & powershell.exe @args
            if ($LASTEXITCODE -ne 0) {
                throw "Start-RalphAutopilot.ps1 failed with exit code $LASTEXITCODE"
            }

            $currentRunState = Read-JsonFile -Path (Get-RalphStateFile -Name "current_run.json")
            $runId = [string]$currentRunState.run_id
            $summary = [string]$currentRunState.latest_message
            $finalPath = Finalize-ProcessedItem -Processed $processed -DestinationRoot $roots.Completed -State "completed" -RunId $runId -Summary $summary
            Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "completed" -Actor "daemon" -Message ("Completed task " + $processed.id) -Data @{
                task_directory = $finalPath
                run_id = $runId
            }
            Send-TelegramNotification `
                -EventName "task_completed" `
                -Title "Task completed" `
                -Message ($processed.title + "`n" + $summary) `
                -RunId $runId `
                -Stage "queue" `
                -Status "completed" | Out-Null
        }
        catch {
            $summary = $_.Exception.Message
            $failedPath = Finalize-ProcessedItem -Processed $processed -DestinationRoot $roots.Failed -State "failed" -RunId $runId -Summary $summary
            Add-RalphEvent -RunId $script:daemonRunId -Stage "queue" -Status "failed" -Actor "daemon" -Message ("Failed task " + $processed.id + ": " + $summary) -Data @{
                task_directory = $failedPath
                run_id = $runId
            }
            Send-TelegramNotification `
                -EventName "task_failed" `
                -Title "Task failed" `
                -Message ($processed.title + "`n" + $summary) `
                -RunId $runId `
                -Stage "queue" `
                -Status "failed" | Out-Null
        }

        if ($MaxTasks -gt 0 -and $script:processedCount -ge $MaxTasks) {
            Save-DaemonState -Status "completed" -Message "MaxTasks limit reached"
            break
        }
    }
}
finally {
    Save-DaemonState -Status "stopped" -Message "Inbox daemon stopped"
    Add-RalphEvent -RunId $script:daemonRunId -Stage "daemon" -Status "stopped" -Actor "daemon" -Message "Ralph inbox daemon stopped"
    Send-TelegramNotification `
        -EventName "daemon_stopped" `
        -Title "Ralph daemon stopped" `
        -Message "Inbox daemon stopped" `
        -RunId $script:daemonRunId `
        -Stage "daemon" `
        -Status "stopped" | Out-Null
    Release-DaemonLock
}
