<#
  Deploy asp-territory to an existing IIS site, locally or over SSH.

  Remote mode:
  - Copies this script to the remote Windows host with scp
  - Executes it remotely via ssh in -RunRemoteCore mode
  - Preserves the remote site's current DB path unless -DbPath is passed
  - Can run standard migrations and an optional legacy migration script

  Local / remote core behavior:
  - Infers IIS site/app pool/work dir from the existing site when possible
  - Stops the site/app pool while deploying
  - Clones/pulls and hard-resets to origin/<Branch>
  - Points IIS at <WorkDir>\public
  - Reapplies the effective DB path in public\web.config
  - Grants IIS AppPool rights to the DB folder
  - Runs migrations
  - Restarts the site/app pool and smoke tests key routes
#>

param(
  [string]$Repo = 'git@onefortheroadgit.sytes.net:dcovington/asp-classic-unified-framework.git',
  [string]$Branch = 'main',
  [string]$SiteName = 'ttasp',
  [string]$AppPool = '',
  [string]$WorkDir = '',
  [string]$PublicDir = '',
  [string]$BaseUrl = '',
  [string]$DbPath = '',

  [switch]$RunMigrations = $true,
  [switch]$SkipLegacyIsBusinessMigration,
  [string]$LegacyMigrationScript = 'scripts\migrate_isbusiness_to_households.vbs',

  [switch]$UseRemoteSsh,
  [string]$RemoteTarget = '',
  [int]$RemotePort = 22,
  [string]$SshExe = 'ssh',
  [string]$ScpExe = 'scp',
  [switch]$RunRemoteCore
)

$ErrorActionPreference = 'Stop'

function Ensure-Dir {
  param([string]$Path)
  if([string]::IsNullOrWhiteSpace($Path)){ return }
  if(!(Test-Path $Path)){
    New-Item -ItemType Directory -Force -Path $Path | Out-Null
  }
}

function Ensure-Command {
  param([string]$Name)
  if(!(Get-Command $Name -ErrorAction SilentlyContinue)){
    throw "$Name not found on PATH"
  }
}

function Get-DefaultRemoteTargetFromInfo {
  $infoPath = Join-Path $PSScriptRoot 'depolyinfo.txt'
  if(!(Test-Path $infoPath)){ return '' }

  $sshLine = Get-Content $infoPath | Where-Object { $_ -match '^\s*ssh\s+' } | Select-Object -First 1
  if([string]::IsNullOrWhiteSpace($sshLine)){ return '' }

  return ($sshLine -replace '^\s*ssh\s+', '').Trim()
}

function ConvertTo-PowerShellLiteral {
  param([AllowNull()][string]$Value)
  if($null -eq $Value){ return "''" }
  return "'" + ($Value -replace "'", "''") + "'"
}

function ConvertTo-CmdDoubleQuoted {
  param([AllowNull()][string]$Value)
  if($null -eq $Value){ return '""' }
  return '"' + ($Value -replace '"', '""') + '"'
}

function Get-DataSourceFromConfig {
  param([string]$ConfigPath)
  if(!(Test-Path $ConfigPath)){ return '' }

  $raw = Get-Content $ConfigPath -Raw
  $match = [regex]::Match($raw, 'Data Source=([^;]+);', [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
  if($match.Success){
    return $match.Groups[1].Value.Trim()
  }

  return ''
}

function Set-DataSourceInConfig {
  param(
    [string]$ConfigPath,
    [string]$EffectiveDbPath
  )

  if(!(Test-Path $ConfigPath)){ return }

  $raw = Get-Content $ConfigPath -Raw
  $updated = [regex]::Replace(
    $raw,
    'Data Source=[^;]*;',
    ('Data Source=' + $EffectiveDbPath + ';'),
    [System.Text.RegularExpressions.RegexOptions]::IgnoreCase
  )

  if($updated -ne $raw){
    Set-Content -Path $ConfigPath -Value $updated -Encoding UTF8
    Write-Host "Updated ConnectionString Data Source to $EffectiveDbPath"
  }
}

function Get-BaseUrlFromSite {
  param($Site)

  $httpBind = $Site.Bindings.Collection | Where-Object { $_.protocol -eq 'http' } | Select-Object -First 1
  if($httpBind){
    $parts = $httpBind.bindingInformation.Split(':')
    $port = $parts[1]
    if([string]::IsNullOrWhiteSpace($port)){ $port = '80' }
    return ('http://127.0.0.1:' + $port)
  }

  return 'http://127.0.0.1'
}

function Invoke-DeployCore {
  Ensure-Command git
  Import-Module WebAdministration

  $site = Get-Website -Name $SiteName
  if(!$site){ throw "IIS site not found: $SiteName" }

  if([string]::IsNullOrWhiteSpace($AppPool)){
    $AppPool = $site.applicationPool
  }

  if([string]::IsNullOrWhiteSpace($PublicDir)){
    $PublicDir = $site.physicalPath
  }

  if([string]::IsNullOrWhiteSpace($WorkDir)){
    $pd = [Environment]::ExpandEnvironmentVariables($PublicDir)
    $pd = $pd.Trim().Trim('"')
    $pd = $pd.TrimEnd('\','/')

    if((Split-Path $pd -Leaf).ToLower() -eq 'public'){
      $WorkDir = Split-Path $pd -Parent
    } else {
      $WorkDir = $pd
    }
  }

  if([string]::IsNullOrWhiteSpace($BaseUrl)){
    $BaseUrl = Get-BaseUrlFromSite -Site $site
  }

  $currentPublicDir = $PublicDir
  $currentConfigPath = Join-Path $currentPublicDir 'web.config'
  $effectiveDbPath = $DbPath
  if([string]::IsNullOrWhiteSpace($effectiveDbPath)){
    $effectiveDbPath = Get-DataSourceFromConfig -ConfigPath $currentConfigPath
  }

  if([string]::IsNullOrWhiteSpace($effectiveDbPath)){
    throw 'No database path was provided and no existing Data Source could be read from the current web.config'
  }

  Write-Host "Stopping IIS site $SiteName and app pool $AppPool"
  try { Stop-Website -Name $SiteName } catch { }
  try { Stop-WebAppPool -Name $AppPool } catch { }

  Ensure-Dir (Split-Path $WorkDir -Parent)
  if((Test-Path $WorkDir) -and !(Test-Path (Join-Path $WorkDir '.git'))){
    $bak = ($WorkDir.TrimEnd('\') + '_pre_git_' + (Get-Date -Format 'yyyyMMdd_HHmmss'))
    Write-Host "Existing non-git folder detected. Moving to $bak"
    Move-Item -Force $WorkDir $bak
  }

  if(!(Test-Path $WorkDir)){
    Write-Host "Cloning $Repo -> $WorkDir"
    git clone $Repo $WorkDir
  }

  Push-Location $WorkDir
  try {
    Write-Host "Updating to origin/$Branch"
    git fetch origin
    git checkout $Branch
    & git reset --hard ("origin/" + $Branch)
  } finally {
    Pop-Location
  }

  if((Split-Path $WorkDir -Leaf).ToLower() -eq 'public'){
    $WorkDir = Split-Path $WorkDir -Parent
  }

  $PublicDir = Join-Path $WorkDir 'public'
  $cfg = Join-Path $PublicDir 'web.config'

  Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name physicalPath -Value $PublicDir
  Set-ItemProperty ('IIS:\Sites\' + $SiteName) -Name applicationPool -Value $AppPool
  Set-ItemProperty ('IIS:\AppPools\' + $AppPool) -Name processModel.identityType -Value NetworkService

  Set-DataSourceInConfig -ConfigPath $cfg -EffectiveDbPath $effectiveDbPath

  $dbFolder = Split-Path $effectiveDbPath -Parent
  if(!(Test-Path $dbFolder)){
    Ensure-Dir $dbFolder
  }
  icacls $dbFolder /grant ("IIS AppPool\" + $AppPool + ":(OI)(CI)(M)") /T | Out-Null

  Push-Location $WorkDir
  try {
    if($RunMigrations){
      Write-Host 'Running standard migrations'
      cscript //nologo scripts\runMigrations.vbs up
    }

    if(-not $SkipLegacyIsBusinessMigration){
      $legacyPath = Join-Path $WorkDir $LegacyMigrationScript
      if(!(Test-Path $legacyPath)){
        throw "Legacy migration script not found: $legacyPath"
      }

      Write-Host 'Running legacy IsBusiness migration'
      cscript //nologo $legacyPath $effectiveDbPath
    }
  } finally {
    Pop-Location
  }

  if((Get-WebAppPoolState -Name $AppPool).Value -eq 'Started'){
    Restart-WebAppPool -Name $AppPool
  } else {
    Start-WebAppPool -Name $AppPool
  }
  Start-Website $SiteName

  Start-Sleep -Seconds 1

  $paths = @('/','/territories','/households','/householder-names')
  foreach($path in $paths){
    $url = $BaseUrl + $path
    $response = Invoke-WebRequest -UseBasicParsing -Uri $url -TimeoutSec 30
    Write-Host ("OK  " + $path + ' -> ' + $response.StatusCode)
  }

  Write-Host 'Deploy complete.'
}

if($UseRemoteSsh -and !$RunRemoteCore -and [string]::IsNullOrWhiteSpace($RemoteTarget)){
  $RemoteTarget = Get-DefaultRemoteTargetFromInfo
}

if($UseRemoteSsh -and !$RunRemoteCore -and -not [string]::IsNullOrWhiteSpace($RemoteTarget)){
  Ensure-Command $SshExe
  Ensure-Command $ScpExe

  $remoteScriptPath = 'C:\Windows\Temp\deploy-test-territory-git.ps1'
  $scpDestination = "${RemoteTarget}:C:/Windows/Temp/deploy-test-territory-git.ps1"

  Write-Host "Copying deploy script to $RemoteTarget"
  & $ScpExe -P $RemotePort $PSCommandPath $scpDestination
  if($LASTEXITCODE -ne 0){ throw 'scp failed' }

  $remoteCommand = New-Object System.Collections.Generic.List[string]
  @(
    'powershell',
    '-NoProfile',
    '-ExecutionPolicy', 'Bypass',
    '-File', (ConvertTo-CmdDoubleQuoted $remoteScriptPath),
    '-RunRemoteCore',
    '-Repo', (ConvertTo-CmdDoubleQuoted $Repo),
    '-Branch', (ConvertTo-CmdDoubleQuoted $Branch),
    '-SiteName', (ConvertTo-CmdDoubleQuoted $SiteName)
  ) | ForEach-Object { [void]$remoteCommand.Add($_) }

  if(-not [string]::IsNullOrWhiteSpace($AppPool)){
    [void]$remoteCommand.Add('-AppPool')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $AppPool))
  }

  if(-not [string]::IsNullOrWhiteSpace($WorkDir)){
    [void]$remoteCommand.Add('-WorkDir')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $WorkDir))
  }

  if(-not [string]::IsNullOrWhiteSpace($PublicDir)){
    [void]$remoteCommand.Add('-PublicDir')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $PublicDir))
  }

  if(-not [string]::IsNullOrWhiteSpace($BaseUrl)){
    [void]$remoteCommand.Add('-BaseUrl')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $BaseUrl))
  }

  if(-not [string]::IsNullOrWhiteSpace($DbPath)){
    [void]$remoteCommand.Add('-DbPath')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $DbPath))
  }

  if(-not [string]::IsNullOrWhiteSpace($LegacyMigrationScript)){
    [void]$remoteCommand.Add('-LegacyMigrationScript')
    [void]$remoteCommand.Add((ConvertTo-CmdDoubleQuoted $LegacyMigrationScript))
  }

  if($RunMigrations){ $remoteCommand += '-RunMigrations' }
  if($SkipLegacyIsBusinessMigration){ $remoteCommand += '-SkipLegacyIsBusinessMigration' }

  Write-Host "Executing remote deploy on $RemoteTarget"
  & $SshExe -p $RemotePort $RemoteTarget ($remoteCommand -join ' ')
  if($LASTEXITCODE -ne 0){ throw 'remote deploy failed' }

  exit 0
}

Invoke-DeployCore
