# Continuous Delivery Script for EasyStream
# Automatically commits and pushes changes to GitHub

param(
    [int]$IntervalSeconds = 300,  # Default: 5 minutes
    [string]$Branch = "dev",
    [string]$CommitPrefix = "auto:",
    [switch]$WatchMode,
    [switch]$Verbose
)

$ErrorActionPreference = "Continue"
$RepoPath = $PSScriptRoot

# Color output functions
function Write-Success { param($msg) Write-Host "✓ $msg" -ForegroundColor Green }
function Write-Info { param($msg) Write-Host "ℹ $msg" -ForegroundColor Cyan }
function Write-Warning { param($msg) Write-Host "⚠ $msg" -ForegroundColor Yellow }
function Write-Error { param($msg) Write-Host "✗ $msg" -ForegroundColor Red }

# Load configuration
$configPath = Join-Path $RepoPath ".cd-config.json"
if (Test-Path $configPath) {
    $config = Get-Content $configPath | ConvertFrom-Json
    if ($config.intervalSeconds) { $IntervalSeconds = $config.intervalSeconds }
    if ($config.branch) { $Branch = $config.branch }
    if ($config.commitPrefix) { $CommitPrefix = $config.commitPrefix }
    if ($config.excludePatterns) { $excludePatterns = $config.excludePatterns }
} else {
    $excludePatterns = @(
        "f_data/data_sessions/*",
        "f_data/data_cache/_c_tpl/*",
        ".setup_complete",
        "*.log",
        "db_data/*",
        "node_modules/*",
        "vendor/*"
    )
}

function Test-GitRepo {
    Push-Location $RepoPath
    try {
        $null = git rev-parse --git-dir 2>&1
        return $?
    } finally {
        Pop-Location
    }
}

function Get-GitStatus {
    Push-Location $RepoPath
    try {
        $status = git status --porcelain 2>&1
        return $status
    } finally {
        Pop-Location
    }
}

function Get-ChangeSummary {
    Push-Location $RepoPath
    try {
        $modified = @(git diff --name-only).Count
        $staged = @(git diff --cached --name-only).Count
        $untracked = @(git ls-files --others --exclude-standard).Count

        return @{
            Modified = $modified
            Staged = $staged
            Untracked = $untracked
            Total = $modified + $staged + $untracked
        }
    } finally {
        Pop-Location
    }
}

function Update-GitIgnore {
    $gitignorePath = Join-Path $RepoPath ".gitignore"

    $ignoreContent = @"
# Temporary session files
f_data/data_sessions/sess_*
# Cache files
f_data/data_cache/_c_tpl/*
# Setup marker
.setup_complete
# Database runtime files
db_data/*
# Logs
*.log
# Dependencies
node_modules/
vendor/
# IDE
.vscode/
.idea/
"@

    if (-not (Test-Path $gitignorePath)) {
        $ignoreContent | Out-File -FilePath $gitignorePath -Encoding UTF8
        Write-Success "Created .gitignore"
    } else {
        # Append if patterns are missing
        $existing = Get-Content $gitignorePath -Raw
        if ($existing -notmatch "f_data/data_sessions/sess_\*") {
            "`n# Auto-generated exclusions`n$ignoreContent" | Add-Content -Path $gitignorePath
            Write-Success "Updated .gitignore"
        }
    }
}

function Invoke-AutoCommit {
    param([string]$message)

    Push-Location $RepoPath
    try {
        Write-Info "Checking for changes..."

        $changes = Get-ChangeSummary

        if ($changes.Total -eq 0) {
            if ($Verbose) { Write-Info "No changes detected" }
            return $false
        }

        Write-Info "Found $($changes.Total) changed files (Modified: $($changes.Modified), Staged: $($changes.Staged), Untracked: $($changes.Untracked))"

        # Stage all changes
        Write-Info "Staging changes..."
        git add -A 2>&1 | Out-Null

        # Generate commit message if not provided
        if (-not $message) {
            $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
            $message = "${CommitPrefix} Update at $timestamp"

            # Add file change summary
            $fileList = @(git diff --cached --name-only | Select-Object -First 5)
            if ($fileList.Count -gt 0) {
                $message += "`n`nChanged files:"
                foreach ($file in $fileList) {
                    $message += "`n- $file"
                }
                if ($changes.Total -gt 5) {
                    $message += "`n- ... and $($changes.Total - 5) more files"
                }
            }

            $message += "`n`n🤖 Generated with Claude Code Continuous Delivery`n`nCo-Authored-By: Claude <noreply@anthropic.com>"
        }

        # Create commit
        Write-Info "Creating commit..."
        $commitResult = git commit -m $message 2>&1

        if ($LASTEXITCODE -eq 0) {
            Write-Success "Committed changes"
            return $true
        } else {
            Write-Warning "Commit failed or nothing to commit"
            if ($Verbose) { Write-Host $commitResult }
            return $false
        }
    } finally {
        Pop-Location
    }
}

function Invoke-AutoPush {
    Push-Location $RepoPath
    try {
        Write-Info "Pushing to origin/$Branch..."

        # Check if we're ahead of remote
        $ahead = git rev-list --count "origin/$Branch..$Branch" 2>&1

        if ($ahead -match "^\d+$" -and [int]$ahead -gt 0) {
            Write-Info "Local is $ahead commit(s) ahead of remote"

            # Push with retry logic
            $maxRetries = 3
            $retryCount = 0

            while ($retryCount -lt $maxRetries) {
                $pushResult = git push origin $Branch 2>&1

                if ($LASTEXITCODE -eq 0) {
                    Write-Success "Successfully pushed to GitHub"
                    return $true
                } else {
                    $retryCount++
                    Write-Warning "Push attempt $retryCount failed"
                    if ($Verbose) { Write-Host $pushResult }

                    if ($retryCount -lt $maxRetries) {
                        Write-Info "Retrying in 5 seconds..."
                        Start-Sleep -Seconds 5
                    }
                }
            }

            Write-Error "Failed to push after $maxRetries attempts"
            return $false
        } else {
            if ($Verbose) { Write-Info "Already up to date with remote" }
            return $false
        }
    } finally {
        Pop-Location
    }
}

function Start-ContinuousDelivery {
    Write-Info "========================================="
    Write-Info "EasyStream Continuous Delivery Started"
    Write-Info "========================================="
    Write-Info "Repository: $RepoPath"
    Write-Info "Branch: $Branch"
    Write-Info "Interval: $IntervalSeconds seconds"
    Write-Info "========================================="
    Write-Info ""

    # Verify git repository
    if (-not (Test-GitRepo)) {
        Write-Error "Not a git repository: $RepoPath"
        exit 1
    }

    # Update .gitignore
    Update-GitIgnore

    $iteration = 0

    while ($true) {
        $iteration++
        $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

        Write-Host "`n[$timestamp] Check #$iteration" -ForegroundColor Magenta

        try {
            # Commit changes
            $committed = Invoke-AutoCommit

            # Push if there was a commit
            if ($committed) {
                Start-Sleep -Seconds 2
                Invoke-AutoPush
            }

            # Wait for next interval
            Write-Info "Next check in $IntervalSeconds seconds... (Press Ctrl+C to stop)"
            Start-Sleep -Seconds $IntervalSeconds

        } catch {
            Write-Error "Error during CD cycle: $_"
            Write-Info "Continuing in 30 seconds..."
            Start-Sleep -Seconds 30
        }
    }
}

function Start-FileWatcher {
    Write-Info "========================================="
    Write-Info "EasyStream File Watcher Started"
    Write-Info "========================================="
    Write-Info "Watching: $RepoPath"
    Write-Info "Branch: $Branch"
    Write-Info "Debounce: 30 seconds after last change"
    Write-Info "========================================="
    Write-Info ""

    # Verify git repository
    if (-not (Test-GitRepo)) {
        Write-Error "Not a git repository: $RepoPath"
        exit 1
    }

    # Update .gitignore
    Update-GitIgnore

    # Create file system watcher
    $watcher = New-Object System.IO.FileSystemWatcher
    $watcher.Path = $RepoPath
    $watcher.IncludeSubdirectories = $true
    $watcher.EnableRaisingEvents = $true

    # Exclude patterns
    $watcher.Filter = "*.*"
    $watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor
                           [System.IO.NotifyFilters]::DirectoryName -bor
                           [System.IO.NotifyFilters]::LastWrite

    $global:lastChangeTime = Get-Date
    $global:changedFiles = @{}

    $onChange = {
        param($sender, $e)

        # Skip excluded patterns
        $relativePath = $e.FullPath.Replace($RepoPath, "").TrimStart('\', '/')
        $exclude = $false
        foreach ($pattern in $excludePatterns) {
            if ($relativePath -like $pattern) {
                $exclude = $true
                break
            }
        }

        if (-not $exclude) {
            $global:lastChangeTime = Get-Date
            $global:changedFiles[$e.FullPath] = $e.ChangeType
            Write-Host "[$(Get-Date -Format 'HH:mm:ss')] " -NoNewline -ForegroundColor Gray
            Write-Host "$($e.ChangeType): " -NoNewline -ForegroundColor Yellow
            Write-Host $relativePath -ForegroundColor White
        }
    }

    # Register events
    Register-ObjectEvent -InputObject $watcher -EventName Changed -Action $onChange | Out-Null
    Register-ObjectEvent -InputObject $watcher -EventName Created -Action $onChange | Out-Null
    Register-ObjectEvent -InputObject $watcher -EventName Deleted -Action $onChange | Out-Null
    Register-ObjectEvent -InputObject $watcher -EventName Renamed -Action $onChange | Out-Null

    Write-Success "File watcher active. Monitoring for changes..."
    Write-Info "Changes will auto-commit 30 seconds after last modification"
    Write-Info ""

    try {
        while ($true) {
            Start-Sleep -Seconds 5

            # Check if enough time has passed since last change
            $timeSinceLastChange = (Get-Date) - $global:lastChangeTime

            if ($global:changedFiles.Count -gt 0 -and $timeSinceLastChange.TotalSeconds -ge 30) {
                Write-Info "`nDebounce period elapsed. Processing $($global:changedFiles.Count) changes..."

                $committed = Invoke-AutoCommit
                if ($committed) {
                    Start-Sleep -Seconds 2
                    Invoke-AutoPush
                }

                # Reset
                $global:changedFiles = @{}
                Write-Info "`nContinuing to watch for changes...`n"
            }
        }
    } finally {
        $watcher.Dispose()
        Get-EventSubscriber | Unregister-Event
    }
}

# Main execution
if ($WatchMode) {
    Start-FileWatcher
} else {
    Start-ContinuousDelivery
}
