[CmdletBinding(SupportsShouldProcess = $true)]
param(
    [string]$OldInstallRoot = 'C:\Program Files\ActivityWatch-Phase2',
    [string]$OldStateRoot = 'C:\ProgramData\ActivityWatch-Phase2',
    [string]$NewInstallRoot = 'C:\Program Files\AWatch-rus\bin',
    [string]$NewStateRoot = 'C:\ProgramData\AWatch-rus',
    [string]$ToolkitRoot = 'C:\Program Files\AWatch-rus\windows',
    [switch]$SkipValidation
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$modulePath = Join-Path $PSScriptRoot 'ActivityWatch.Windows.Common.psm1'
Import-Module $modulePath -Force

Assert-Administrator

function Copy-DirectoryContents {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Source,
        [Parameter(Mandatory = $true)]
        [string]$Destination
    )

    if (-not (Test-Path -LiteralPath $Source)) {
        return
    }

    New-ActivityWatchDirectory -Path $Destination
    Copy-Item -Path (Join-Path $Source '*') -Destination $Destination -Recurse -Force
}

function Copy-IfExists {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Source,
        [Parameter(Mandatory = $true)]
        [string]$Destination
    )

    if (Test-Path -LiteralPath $Source) {
        Copy-Item -LiteralPath $Source -Destination $Destination -Force
    }
}

function Convert-PathValue {
    param(
        [AllowNull()]
        [string]$Value
    )

    if ([string]::IsNullOrWhiteSpace($Value)) {
        return $Value
    }

    return $Value.Replace($OldInstallRoot, $NewInstallRoot).Replace($OldStateRoot, $NewStateRoot)
}

function Stop-AWatchTaskSet {
    foreach ($task in @(Get-ScheduledTask | Where-Object { $_.TaskName -eq 'ActivityWatch Recovery' -or $_.TaskName -like 'ActivityWatch Launch *' })) {
        Stop-ScheduledTask -TaskName $task.TaskName -ErrorAction SilentlyContinue
    }
}

function Get-ExistingAWatchConfig {
    $newConfigPath = Join-Path $NewStateRoot 'deployment-config.json'
    $oldConfigPath = Join-Path $OldStateRoot 'deployment-config.json'

    if (Test-Path -LiteralPath $oldConfigPath) {
        return [pscustomobject]@{
            Path   = $oldConfigPath
            Config = Read-ActivityWatchDeploymentConfig -Path $oldConfigPath
        }
    }

    if (Test-Path -LiteralPath $newConfigPath) {
        return [pscustomobject]@{
            Path   = $newConfigPath
            Config = Read-ActivityWatchDeploymentConfig -Path $newConfigPath
        }
    }

    throw "Не найден deployment-config.json ни в $OldStateRoot, ни в $NewStateRoot."
}

function Update-AWatchConfigPaths {
    param(
        [Parameter(Mandatory = $true)]
        [pscustomobject]$Config
    )

    function Resolve-OptionalCollectorRuntimePath {
        param([Parameter(Mandatory = $true)][string]$FileName)

        $stateCandidate = Join-Path $NewStateRoot $FileName
        if (Test-Path -LiteralPath $stateCandidate) {
            return $stateCandidate
        }

        if (Test-Path -LiteralPath (Join-Path $ToolkitRoot $FileName)) {
            return $stateCandidate
        }

        return ''
    }

    function Set-ConfigPathValue {
        param(
            [Parameter(Mandatory = $true)][string]$Name,
            [AllowEmptyString()][string]$Value
        )

        if ($Config.paths.PSObject.Properties.Name -contains $Name) {
            $Config.paths.PSObject.Properties[$Name].Value = $Value
        }
        else {
            $Config.paths | Add-Member -NotePropertyName $Name -NotePropertyValue $Value
        }
    }

    $logsRoot = Join-Path $NewStateRoot 'logs'
    $Config.paths.installRoot = $NewInstallRoot
    $Config.paths.stateRoot = $NewStateRoot
    $Config.paths.logsRoot = $logsRoot
    Set-ConfigPathValue -Name 'collectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'browser-domains-native-collector.ps1')
    Set-ConfigPathValue -Name 'endpointCollectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'dlp-endpoint-signals-collector.ps1')
    Set-ConfigPathValue -Name 'fileCollectorScript' -Value (Resolve-OptionalCollectorRuntimePath -FileName 'file-operations-collector.ps1')
    if ($Config.paths.PSObject.Properties.Name -contains 'file1cTelemetryExecutable') {
        $Config.paths.file1cTelemetryExecutable = Join-Path $ToolkitRoot 'aw-windows-telemetry.exe'
    }
    else {
        $Config.paths | Add-Member -NotePropertyName 'file1cTelemetryExecutable' -NotePropertyValue (Join-Path $ToolkitRoot 'aw-windows-telemetry.exe')
    }
    if ($Config.paths.PSObject.Properties.Name -contains 'sessionCollectorScript') {
        $Config.paths.sessionCollectorScript = Join-Path $NewStateRoot 'worktime-session-collector.ps1'
    }
    $Config.paths.rulesPath = Join-Path $NewStateRoot 'web-category-rules.json'
    if ($Config.paths.PSObject.Properties.Name -contains 'policyPath') {
        $Config.paths.policyPath = Join-Path $NewStateRoot 'dlp-policy.json'
    }
    if ($Config.PSObject.Properties.Name -contains 'collectors') {
        foreach ($entry in @(
                @{ Name = 'browserCollectorMode'; Value = 'rust_primary' },
                @{ Name = 'dlpEndpointMode'; Value = 'rust_primary' },
                @{ Name = 'fileOpsMode'; Value = 'rust_primary' }
            )) {
            if ($Config.collectors.PSObject.Properties.Name -contains $entry.Name) {
                $Config.collectors.PSObject.Properties[$entry.Name].Value = $entry.Value
            }
            else {
                $Config.collectors | Add-Member -NotePropertyName $entry.Name -NotePropertyValue $entry.Value
            }
        }
    }
    $Config.paths.launchScript = Join-Path $NewStateRoot 'launch-watchers.ps1'
    $Config.paths.recoveryScript = Join-Path $NewStateRoot 'recovery-loop.ps1'

    if ($Config.PSObject.Properties.Name -contains 'incidentCapture' -and $Config.incidentCapture.PSObject.Properties.Name -contains 'artifactsRoot') {
        $Config.incidentCapture.artifactsRoot = Convert-PathValue -Value ([string]$Config.incidentCapture.artifactsRoot)
    }

    return $Config
}

$existing = Get-ExistingAWatchConfig
$backupRoot = Join-Path $NewStateRoot ('migration-backups\' + (Get-Date -Format 'yyyyMMdd-HHmmss'))
$newConfigPath = Join-Path $NewStateRoot 'deployment-config.json'
$newLogsRoot = Join-Path $NewStateRoot 'logs'

$summary = [ordered]@{
    sourceConfig = $existing.Path
    oldInstallRoot = $OldInstallRoot
    oldStateRoot = $OldStateRoot
    newInstallRoot = $NewInstallRoot
    newStateRoot = $NewStateRoot
    backupRoot = $backupRoot
    actions = @(
        'stop ActivityWatch scheduled tasks',
        'backup old/new install and state directories',
        'copy old install/state contents to AWatch-rus paths',
        'rewrite deployment-config.json paths',
        'regenerate launcher/recovery scripts',
        're-register scheduled tasks',
        'run validate-deployment.ps1'
    )
}

if ($WhatIfPreference) {
    return [pscustomobject]$summary
}

if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'Миграция ActivityWatch Windows/RDP путей в AWatch-rus')) {
    New-ActivityWatchDirectory -Path $NewStateRoot
    New-ActivityWatchDirectory -Path $backupRoot

    Stop-AWatchTaskSet

    foreach ($item in @(
            @{ Source = $OldInstallRoot; Name = 'old-install' },
            @{ Source = $OldStateRoot; Name = 'old-state' },
            @{ Source = $NewInstallRoot; Name = 'new-install' },
            @{ Source = $NewStateRoot; Name = 'new-state' }
        )) {
        if (Test-Path -LiteralPath $item.Source) {
            $backupDest = Join-Path $backupRoot $item.Name
            New-ActivityWatchDirectory -Path $backupDest

            $excludeDirs = @()
            if ($item.Source -eq $NewStateRoot) {
                # Avoid infinite recursion: backupRoot is inside NewStateRoot by default.
                $excludeDirs += $backupRoot
            }

            $robocopyArgs = @(
                $item.Source,
                $backupDest,
                '/E',
                '/R:1',
                '/W:1',
                '/NFL',
                '/NDL',
                '/NJH',
                '/NJS',
                '/NP'
            )
            if ($excludeDirs.Count -gt 0) {
                $robocopyArgs += '/XD'
                $robocopyArgs += $excludeDirs
            }

            & robocopy @robocopyArgs | Out-Null
            if ($LASTEXITCODE -ge 8) {
                throw "Backup robocopy failed (exit=$LASTEXITCODE) for source '$($item.Source)' to '$backupDest'"
            }
        }
    }

    Copy-DirectoryContents -Source $OldInstallRoot -Destination $NewInstallRoot
    Copy-DirectoryContents -Source $OldStateRoot -Destination $NewStateRoot
    New-ActivityWatchDirectory -Path $newLogsRoot

    foreach ($file in @(
            'browser-domains-native-collector.ps1',
            'dlp-endpoint-signals-collector.ps1',
            'file-operations-collector.ps1',
            'worktime-session-collector.ps1',
            'aw-windows-telemetry.exe',
            'web-category-rules.example.json',
            'dlp-policy.example.json'
        )) {
        Copy-IfExists -Source (Join-Path $ToolkitRoot $file) -Destination (Join-Path $NewStateRoot $file)
    }

    Copy-IfExists -Source (Join-Path $OldStateRoot 'web-category-rules.json') -Destination (Join-Path $NewStateRoot 'web-category-rules.json')
    Copy-IfExists -Source (Join-Path $OldStateRoot 'dlp-policy.json') -Destination (Join-Path $NewStateRoot 'dlp-policy.json')
    if (-not (Test-Path -LiteralPath (Join-Path $NewStateRoot 'web-category-rules.json'))) {
        Copy-IfExists -Source (Join-Path $NewStateRoot 'web-category-rules.example.json') -Destination (Join-Path $NewStateRoot 'web-category-rules.json')
    }
    if (-not (Test-Path -LiteralPath (Join-Path $NewStateRoot 'dlp-policy.json'))) {
        Copy-IfExists -Source (Join-Path $NewStateRoot 'dlp-policy.example.json') -Destination (Join-Path $NewStateRoot 'dlp-policy.json')
    }

    $config = Update-AWatchConfigPaths -Config $existing.Config
    Write-ActivityWatchDeploymentConfig -Config $config -Path $newConfigPath
    Write-ActivityWatchLaunchScript -Path $config.paths.launchScript -ConfigPath $newConfigPath
    Write-ActivityWatchRecoveryScript -Path $config.paths.recoveryScript -ConfigPath $newConfigPath

    $taskDefinitions = @($config.userTasks)
    Set-ActivityWatchAcl -InstallRoot $NewInstallRoot -StateRoot $NewStateRoot -LogsRoot $newLogsRoot
    Register-ActivityWatchUserTasks -TaskDefinitions $taskDefinitions -LaunchScriptPath $config.paths.launchScript -ConfigPath $newConfigPath
    Register-ActivityWatchRecoveryTask -TaskName $config.recovery.taskName -RecoveryScriptPath $config.paths.recoveryScript -ConfigPath $newConfigPath
    Start-ActivityWatchTasks -TaskDefinitions $taskDefinitions -RecoveryTaskName $config.recovery.taskName
    Start-Sleep -Seconds 5

    if (-not $SkipValidation) {
        $validateScript = Join-Path $ToolkitRoot 'validate-deployment.ps1'
        if (-not (Test-Path -LiteralPath $validateScript)) {
            $validateScript = Join-Path $PSScriptRoot 'validate-deployment.ps1'
        }
        $report = & $validateScript -ConfigPath $newConfigPath
        if (-not [bool]$report.overallOk) {
            throw "Миграция выполнена, но validation завершился ошибкой. Backup: $backupRoot"
        }
    }

    [pscustomobject]@{
        migrated = $true
        backupRoot = $backupRoot
        configPath = $newConfigPath
        installRoot = $NewInstallRoot
        stateRoot = $NewStateRoot
    }
}
