# ============================================================================
# EasyStream - Folder Sync Script (Repos -> Docker-Progs)
# ============================================================================
# This script syncs changes from E:\repos\easystream-main to E:\docker-progs\easystream-main
#
# Usage:
#   .\sync-to-docker-progs.ps1              # One-time sync
#   .\sync-to-docker-progs.ps1 -Watch       # Continuous monitoring
#   .\sync-to-docker-progs.ps1 -Verbose     # Detailed output
#
# Requirements: PowerShell 5.0 or higher
# ============================================================================

param(
    [switch]$Watch = $false,
    [switch]$Verbose = $false,
    [switch]$DryRun = $false
)

# Configuration
$SourcePath = "E:\repos\easystream-main"
$DestPath = "E:\docker-progs\easystream-main"
$LogFile = "E:\repos\easystream-main\sync.log"

# Exclusions (paths to ignore)
$Exclusions = @(
    ".git",
    ".gitignore",
    "node_modules",
    "vendor",
    "f_data\cache",
    "f_data\tmp",
    "f_data\logs",
    "f_data\sessions",
    "f_data\uploads",
    "*.log",
    "sync.log",
    "sync-to-docker-progs.ps1"
)

# ============================================================================
# Functions
# ============================================================================

function Write-Log {
    param([string]$Message, [string]$Level = "INFO")

    $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $logMessage = "[$timestamp] [$Level] $Message"

    # Write to console
    switch ($Level) {
        "ERROR" { Write-Host $logMessage -ForegroundColor Red }
        "WARN"  { Write-Host $logMessage -ForegroundColor Yellow }
        "SUCCESS" { Write-Host $logMessage -ForegroundColor Green }
        default { Write-Host $logMessage -ForegroundColor White }
    }

    # Write to log file
    Add-Content -Path $LogFile -Value $logMessage
}

function Test-ShouldExclude {
    param([string]$Path)

    foreach ($exclusion in $Exclusions) {
        if ($Path -like "*$exclusion*") {
            return $true
        }
    }
    return $false
}

function Sync-Folder {
    param([bool]$InitialSync = $false)

    try {
        # Check if source exists
        if (-not (Test-Path $SourcePath)) {
            Write-Log "Source path does not exist: $SourcePath" "ERROR"
            return $false
        }

        # Create destination if it doesn't exist
        if (-not (Test-Path $DestPath)) {
            Write-Log "Creating destination directory: $DestPath" "INFO"
            if (-not $DryRun) {
                New-Item -ItemType Directory -Path $DestPath -Force | Out-Null
            }
        }

        # Build robocopy exclusion parameters
        $excludeDirs = @()
        $excludeFiles = @()

        foreach ($exclusion in $Exclusions) {
            if ($exclusion.Contains("\")) {
                $excludeDirs += $exclusion
            } elseif ($exclusion.StartsWith("*")) {
                $excludeFiles += $exclusion
            } else {
                $excludeDirs += $exclusion
            }
        }

        # Build robocopy command
        $robocopyArgs = @(
            $SourcePath,
            $DestPath,
            "/MIR",           # Mirror (delete files in dest that don't exist in source)
            "/R:3",           # Retry 3 times
            "/W:5",           # Wait 5 seconds between retries
            "/MT:8",          # Multi-threaded (8 threads)
            "/NFL",           # No file list
            "/NDL",           # No directory list
            "/NP",            # No progress
            "/BYTES"          # Show sizes in bytes
        )

        # Add exclusions
        if ($excludeDirs.Count -gt 0) {
            $robocopyArgs += "/XD"
            $robocopyArgs += $excludeDirs
        }

        if ($excludeFiles.Count -gt 0) {
            $robocopyArgs += "/XF"
            $robocopyArgs += $excludeFiles
        }

        # Add verbose flag if requested
        if ($Verbose) {
            $robocopyArgs = $robocopyArgs | Where-Object { $_ -ne "/NFL" -and $_ -ne "/NDL" }
        }

        # Execute sync
        if ($InitialSync) {
            Write-Log "Starting initial sync..." "INFO"
        } else {
            Write-Log "Syncing changes..." "INFO"
        }

        if ($DryRun) {
            Write-Log "DRY RUN - Would execute: robocopy $($robocopyArgs -join ' ')" "WARN"
            return $true
        }

        $result = & robocopy $robocopyArgs
        $exitCode = $LASTEXITCODE

        # Robocopy exit codes:
        # 0 = No files copied
        # 1 = Files copied successfully
        # 2 = Extra files or directories detected
        # 3 = Files copied + extra files detected
        # 4+ = Error

        if ($exitCode -ge 8) {
            Write-Log "Sync failed with exit code: $exitCode" "ERROR"
            return $false
        } elseif ($exitCode -gt 0) {
            Write-Log "Sync completed successfully (Exit code: $exitCode)" "SUCCESS"
            return $true
        } else {
            if ($Verbose) {
                Write-Log "No changes detected" "INFO"
            }
            return $true
        }

    } catch {
        Write-Log "Sync error: $($_.Exception.Message)" "ERROR"
        return $false
    }
}

function Start-Watcher {
    Write-Log "Starting file system watcher..." "INFO"
    Write-Log "Monitoring: $SourcePath" "INFO"
    Write-Log "Syncing to: $DestPath" "INFO"
    Write-Log "Press Ctrl+C to stop..." "WARN"

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

    # Filters
    $watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor
                           [System.IO.NotifyFilters]::DirectoryName -bor
                           [System.IO.NotifyFilters]::LastWrite -bor
                           [System.IO.NotifyFilters]::Size

    # Debounce mechanism (prevent multiple syncs for rapid changes)
    $script:lastSync = Get-Date
    $script:syncPending = $false
    $debounceSeconds = 2

    # Event handler
    $onChange = {
        param($sender, $e)

        # Check if file should be excluded
        if (Test-ShouldExclude -Path $e.FullPath) {
            return
        }

        $now = Get-Date
        $timeSinceLastSync = ($now - $script:lastSync).TotalSeconds

        if ($timeSinceLastSync -gt $debounceSeconds) {
            Write-Log "Change detected: $($e.ChangeType) - $($e.Name)" "INFO"
            Sync-Folder | Out-Null
            $script:lastSync = Get-Date
        } else {
            if (-not $script:syncPending) {
                $script:syncPending = $true
                Write-Log "Changes detected, sync scheduled..." "INFO"
            }
        }
    }

    # 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

    # Timer for debounced syncs
    $timer = New-Object System.Timers.Timer
    $timer.Interval = $debounceSeconds * 1000
    $timer.AutoReset = $true

    $onTimer = {
        if ($script:syncPending) {
            Sync-Folder | Out-Null
            $script:syncPending = $false
            $script:lastSync = Get-Date
        }
    }

    Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $onTimer | Out-Null
    $timer.Start()

    # Keep script running
    try {
        while ($true) {
            Start-Sleep -Seconds 1
        }
    } finally {
        # Cleanup
        $watcher.EnableRaisingEvents = $false
        $watcher.Dispose()
        $timer.Stop()
        $timer.Dispose()
        Get-EventSubscriber | Unregister-Event
        Write-Log "Watcher stopped" "INFO"
    }
}

# ============================================================================
# Main Execution
# ============================================================================

Write-Host ""
Write-Host "============================================================================" -ForegroundColor Cyan
Write-Host "  EasyStream Folder Sync - Repos to Docker-Progs" -ForegroundColor Cyan
Write-Host "============================================================================" -ForegroundColor Cyan
Write-Host ""

# Initial sync
$syncResult = Sync-Folder -InitialSync $true

if (-not $syncResult) {
    Write-Log "Initial sync failed. Exiting." "ERROR"
    exit 1
}

# Watch mode
if ($Watch) {
    Write-Host ""
    Start-Watcher
} else {
    Write-Host ""
    Write-Log "Single sync completed. Use -Watch for continuous monitoring." "INFO"
    Write-Host ""
}
