#Requires -Version 5.1

[CmdletBinding()]
param ()

Set-StrictMode -Version 3.0

$scriptPath = $MyInvocation.MyCommand.Path
$scriptDir = Split-Path -Path $scriptPath -Parent
$logDir = Join-Path -Path $scriptDir -ChildPath ".logs"
$logName = "foam_sync.log"
$logFilePath = Join-Path -Path $logDir -ChildPath $logName
$scriptName = (Get-Item $scriptPath).Name

# --- Task Setup ---
$taskName = 'FoamGitSync'
$taskDescription = "Periodically synchronizes the Git repository at $scriptDir using $scriptName."
$taskExecute = 'c:\windows\system32\conhost.exe'
$taskArguments = "--headless powershell.exe -WindowStyle Hidden -NoProfile -NonInteractive -ExecutionPolicy Bypass -File $scriptPath -Verbose"
$frequencyMinutes = 2
$timeLimitSeconds = 30

if (-not (Test-Path -Path $logDir)) {
  New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
Start-Transcript -Path $logFilePath -IncludeInvocationHeader -Force

try {
  Write-Verbose "Script: $scriptName at $scriptPath"
  Write-Verbose "Repository directory: $scriptDir"
  Write-Verbose "Sync frequency: Every $frequencyMinutes minutes"
  Write-Verbose "Log file: $logFilePath"

  # Run as the user who executes this script.
  $taskPrincipal = New-ScheduledTaskPrincipal -UserId $env:USERNAME -LogonType Interactive
  $repetitionInterval = New-TimeSpan -Minutes $frequencyMinutes
  $trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval $repetitionInterval
  $action = New-ScheduledTaskAction -Execute $taskExecute -Argument $taskArguments
  $settings = New-ScheduledTaskSettingsSet -ExecutionTimeLimit $(New-TimeSpan -Seconds $timeLimitSeconds)
  Write-Verbose "Task name: $taskName"
  Write-Verbose "Description: $taskDescription"
  Write-Verbose "Execute: $taskExecute"
  Write-Verbose "Arguments: $taskArguments"
  Write-Verbose "Frequency: $frequencyMinutes minutes"
  Write-Verbose "Time limit: $timeLimitSeconds seconds"
  Write-Verbose "Trigger: $trigger"
  Write-Verbose "Action: $action"
  Write-Verbose "Principal: $taskPrincipal"
  Write-Verbose "Settings: $settings"

  # Check and configure the scheduled task
  try {
    $existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue

    if ($existingTask) {
      Write-Verbose "Scheduled task '$taskName' already exists. Checking configuration..."
      $currentTrigger = $existingTask.Triggers[0]
      $currentAction = $existingTask.Actions[0]

      # Check Trigger
      Write-Verbose "Checking trigger..."
      $triggerMatches = $false
      if ($currentTrigger -is [Microsoft.Management.Infrastructure.CimInstance] `
          -and $currentTrigger.Repetition.Interval -eq $trigger.Repetition.Interval) {
        $triggerMatches = $true
      }
      else {
        Write-Warning "trigger does not match:`n $(Compare-Object -IncludeEqual -ReferenceObject $currentTrigger -DifferenceObject $trigger)"
      }

      # Check Action
      Write-Verbose "Checking action..."
      $actionMatches = $false
      if ($currentAction -is [Microsoft.Management.Infrastructure.CimInstance] `
          -and $currentAction.Execute -eq $taskExecute `
          -and $currentAction.Arguments -eq $taskArguments) {
        $actionMatches = $true
      }
      else {
        Write-Warning "action does not match:"
        Write-Warning "  Current action: $($currentAction.Execute) $($currentAction.Arguments)"
        Write-Warning "  Expected action: $taskExecute $taskArguments"
      }

      # Check Principal
      Write-Verbose "Checking principal..."
      $principalMatches = ($existingTask.Principal.UserId -eq $taskPrincipal.UserId)

      # Check Settings (specifically ExecutionTimeLimit for this change)
      $settingsMatch = $false
      if ($existingTask.Settings.ExecutionTimeLimit -eq $settings.ExecutionTimeLimit) {
        $settingsMatch = $true
      }
      else {
        Write-Warning "settings do not match: want $($settings.ExecutionTimeLimit), got $($existingTask.Settings.ExecutionTimeLimit)"
      }

      if ($triggerMatches -and $actionMatches -and $principalMatches -and $settingsMatch) {
        Write-Verbose "Scheduled task '$taskName' is already correctly configured."
      }
      else {
        Write-Verbose "Scheduled task '$taskName' configuration differs."
        Write-Verbose "`$triggerMatches: $triggerMatches, `$actionMatches: $actionMatches, `$principalMatches: $principalMatches, `$settingsMatch: $settingsMatch"
        Write-Verbose "Attempting to update in-place..."
        try {
          Set-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Principal $taskPrincipal -ErrorAction Stop
          Write-Verbose "Scheduled task '$taskName' updated successfully."
        }
        catch {
          Write-Warning "Failed to update scheduled task '$taskName' in-place. Error: $($_.Exception.Message)"
          Write-Warning "The task remains in its previous state. Manual intervention may be required or re-run with Administrator privileges."
        }
      }
    }
    else {
      Write-Verbose "Creating scheduled task '$taskName'..."
      try {
        Register-ScheduledTask -TaskName $taskName -Description $taskDescription -Principal $taskPrincipal -Trigger $trigger -Action $action -Settings $settings -ErrorAction Stop
        Write-Verbose "Scheduled task '$taskName' created successfully."
      }
      catch {
        Write-Warning "Failed to create scheduled task '$taskName'. Error: $($_.Exception.Message)"
        Write-Warning "You may need to run this script as Administrator."
      }
    }
  }
  catch {
    # This outer catch is for unexpected errors, e.g., if Get-ScheduledTask had -ErrorAction Stop
    Write-Warning "An unexpected error occurred during scheduled task setup for '$taskName'. Error: $($_.Exception.Message)"
  }

  # --- Git Operations ---
  Write-Information "Navigating to repository: $scriptDir"
  try {
    Set-Location -Path $scriptDir -ErrorAction Stop
  }
  catch {
    Write-Error "Unable to find repository at $scriptDir. Exiting script."
    exit 1
  }

  Write-Information "Staging all changes with 'git add .'"
  git add .
  if ($LASTEXITCODE -ne 0) {
    Write-Warning "'git add .' command failed with exit code $LASTEXITCODE."
  }

  Write-Information "Checking for staged changes with 'git diff --staged --quiet'..."
  git diff --staged --quiet
  $changesStaged = ($LASTEXITCODE -ne 0)

  if ($changesStaged) {
    Write-Information "Staged changes detected. Creating commit..."
    $commitMessage = "$scriptName ($($env:COMPUTERNAME)) $(Get-Date -Format 'yyyy-MM-ddTHH:mm:ssZ')"
    Write-Information "Commit message: $commitMessage"
    git commit -m $commitMessage
    if ($LASTEXITCODE -ne 0) {
      Write-Warning "'git commit' command failed with exit code $LASTEXITCODE."
    }
    else {
      Write-Information "Commit created successfully."
    }
  }
  else {
    Write-Information "No relevant changes detected to commit."
    Write-Information "Resetting staging area with 'git reset HEAD --quiet'."
    git reset HEAD --quiet
  }

  Write-Information "Updating remote 'origin' with 'git remote update origin --prune'..."
  git remote update origin --prune
  if ($LASTEXITCODE -ne 0) {
    Write-Error "Unable to update remote 'origin'. Exiting script."
    exit 1
  }

  $localCommit = (git rev-parse HEAD 2>$null).Trim()
  if ($LASTEXITCODE -ne 0 -or -not $localCommit) { Write-Error "Failed to get local HEAD commit. Exiting script."; exit 1 }

  $remoteBranch = "origin/main"
  $remoteCommit = (git rev-parse $remoteBranch 2>$null).Trim()
  if ($LASTEXITCODE -ne 0 -or -not $remoteCommit) { Write-Error "Failed to get remote '$remoteBranch' commit. Exiting script."; exit 1 }

  Write-Information "Local HEAD commit: $localCommit"
  Write-Information "Remote '$remoteBranch' commit: $remoteCommit"

  if ($localCommit -eq $remoteCommit) {
    Write-Information "Local and remote are already in sync."
    exit 0
  }

  $sleepyTime = Get-Random -Minimum 1 -Maximum 15
  Write-Information "Local and remote differ. Sleeping for $sleepyTime seconds..."
  Start-Sleep -Seconds $sleepyTime

  $localCommitAfterSleep = (git rev-parse HEAD 2>$null).Trim()
  if ($LASTEXITCODE -ne 0 -or -not $localCommitAfterSleep) { Write-Error "Failed to re-get local HEAD commit. Exiting script."; exit 1 }
  $remoteCommitAfterSleep = (git rev-parse $remoteBranch 2>$null).Trim()
  if ($LASTEXITCODE -ne 0 -or -not $remoteCommitAfterSleep) { Write-Error "Failed to re-get remote '$remoteBranch' commit. Exiting script."; exit 1 }

  if ($localCommitAfterSleep -eq $remoteCommitAfterSleep) {
    Write-Information "Local and remote became synchronized during sleep."
    exit 0
  }
  $localCommit = $localCommitAfterSleep
  $remoteCommit = $remoteCommitAfterSleep

  Write-Information "Proceeding with sync logic..."

  git merge-base --is-ancestor $localCommit $remoteCommit
  $localIsAncestorOfRemote = ($LASTEXITCODE -eq 0)

  git merge-base --is-ancestor $remoteCommit $localCommit
  $remoteIsAncestorOfLocal = ($LASTEXITCODE -eq 0)

  if ($localIsAncestorOfRemote) {
    Write-Information "Remote '$remoteBranch' is ahead. Pulling with rebase..."
    git pull --rebase origin main
    if ($LASTEXITCODE -ne 0) { Write-Error "'git pull --rebase' failed. Manual intervention may be required."; exit 1 }
  }
  elseif ($remoteIsAncestorOfLocal) {
    Write-Information "Local HEAD is ahead. Pushing..."
    git push origin main
    if ($LASTEXITCODE -ne 0) { Write-Error "'git push' failed. Manual intervention may be required."; exit 1 }
  }
  else {
    Write-Information "Local HEAD and remote '$remoteBranch' have diverged."
    $remoteTimestampStr = (git log --pretty=format:"%at" -n 1 $remoteBranch 2>$null).Trim()
    if ($LASTEXITCODE -ne 0 -or -not $remoteTimestampStr) { Write-Error "Failed to get remote commit timestamp for '$remoteBranch'. Exiting script."; exit 1 }
    $remoteTimestamp = [long]$remoteTimestampStr

    $localTimestampStr = (git log --pretty=format:"%at" -n 1 HEAD 2>$null).Trim()
    if ($LASTEXITCODE -ne 0 -or -not $localTimestampStr) { Write-Error "Failed to get local commit timestamp for HEAD. Exiting script."; exit 1 }
    $localTimestamp = [long]$localTimestampStr

    Write-Verbose "Local timestamp: $localTimestamp, Remote timestamp: $remoteTimestamp"

    if ($remoteTimestamp -gt $localTimestamp) {
      Write-Information "Remote is newer. Pulling with rebase, strategy 'theirs'..."
      git pull --rebase -X theirs origin main
    }
    else {
      Write-Information "Local is newer or same age. Pulling with rebase, strategy 'ours'..."
      git pull --rebase -X ours origin main
    }

    if ($LASTEXITCODE -ne 0) { Write-Error "Rebase during divergence failed. Manual intervention may be required."; exit 1 }

    Write-Information "Pushing changes after rebase..."
    git push origin main
    if ($LASTEXITCODE -ne 0) { Write-Error "'git push' after rebase failed. Manual intervention may be required."; exit 1 }
  }

  Write-Information "Synchronization process completed successfully."
  exit 0
}
finally {
  Stop-Transcript
}
