[CmdletBinding()]
param(
    [string]$SourcePath = '',
    [Parameter(Mandatory = $true)]
    [string]$TargetPath,
    [switch]$Apply,
    [switch]$Mirror,
    [string]$LogPath = ''
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8

if ([string]::IsNullOrWhiteSpace($SourcePath)) {
    $SourcePath = Split-Path -Path $PSScriptRoot -Parent
}

function Resolve-DeployPath {
    param(
        [string]$Path,
        [string]$ParameterName
    )

    if ([string]::IsNullOrWhiteSpace($Path)) {
        throw "$ParameterName is required."
    }

    $resolved = Resolve-Path -LiteralPath $Path -ErrorAction SilentlyContinue
    if ($null -ne $resolved) {
        return $resolved.Path
    }

    if ($ParameterName -eq 'TargetPath') {
        return $Path
    }

    throw "$ParameterName could not be resolved: $Path"
}

function Get-DeployLogPath {
    param([string]$RequestedPath)

    if (-not [string]::IsNullOrWhiteSpace($RequestedPath)) {
        $directory = Split-Path -Path $RequestedPath -Parent
        if ($directory -ne '' -and -not (Test-Path -LiteralPath $directory)) {
            New-Item -ItemType Directory -Path $directory -Force | Out-Null
        }

        return $RequestedPath
    }

    $root = Join-Path (Split-Path -Path $PSScriptRoot -Parent) 'storage\maintenance_exports\deploy-robocopy'
    if (-not (Test-Path -LiteralPath $root)) {
        New-Item -ItemType Directory -Path $root -Force | Out-Null
    }

    return (Join-Path $root ('robocopy-' + (Get-Date -Format 'yyyyMMdd-HHmmss') + '.log'))
}

$resolvedSourcePath = Resolve-DeployPath -Path $SourcePath -ParameterName 'SourcePath'
$resolvedTargetPath = Resolve-DeployPath -Path $TargetPath -ParameterName 'TargetPath'
$resolvedLogPath = Get-DeployLogPath -RequestedPath $LogPath

$excludedDirectories = @(
    'storage\sessions',
    'storage\uploads\payment_slips',
    'storage\database_backups',
    'storage\maintenance_exports'
)

$excludedFiles = @(
    '.env',
    'storage\settings\login_throttle.json',
    'storage\settings\runtime_schema_bootstrap.json'
)

$copyMode = if ($Mirror) { '/MIR' } else { '/E' }
$previewMode = if ($Apply) { @() } else { @('/L') }
$modeLabel = if ($Apply) { 'apply' } else { 'preview' }

$arguments = @(
    $resolvedSourcePath,
    $resolvedTargetPath,
    $copyMode,
    '/R:2',
    '/W:2',
    '/NFL',
    '/NDL',
    '/NP'
) + $previewMode + @(
    '/XD'
) + $excludedDirectories + @(
    '/XF'
) + $excludedFiles

Write-Output ('mode: ' + $modeLabel)
Write-Output ('source: ' + $resolvedSourcePath)
Write-Output ('target: ' + $resolvedTargetPath)
Write-Output ('copy_mode: ' + $copyMode)
Write-Output ('log: ' + $resolvedLogPath)
Write-Output ('excluded_directories: ' + ($excludedDirectories -join ', '))
Write-Output ('excluded_files: ' + ($excludedFiles -join ', '))
Write-Output ''
Write-Output ('command: robocopy ' + (($arguments | ForEach-Object {
    if ($_ -match '\s') { '"' + $_ + '"' } else { $_ }
}) -join ' '))
Write-Output ''

if (-not $Apply) {
    Write-Output 'preview_only: use -Apply after you review the robocopy plan.'
}

Start-Transcript -Path $resolvedLogPath -Force | Out-Null
try {
    $null = & robocopy @arguments
    $exitCode = $LASTEXITCODE
} finally {
    Stop-Transcript | Out-Null
}

Write-Output ('robocopy_exit_code: ' + $exitCode)

if ($exitCode -ge 8) {
    throw "Robocopy failed with exit code $exitCode"
}
