<#
.SYNOPSIS
  Sincronizare de inceput de sesiune pentru un proiect ROA (flux SVN + git text FoxBin2Prg).

.DESCRIPTION
  Traieste in COMUN\scripts\ si se distribuie automat prin svn update (fiecare proiect isi
  ruleaza copia lui: <ProjectRoot>\COMUN\scripts\roa_sync.ps1, de regula prin roa_sync.bat
  din radacina proiectului). Pasul 2 depinde de tooling-ul din D:\ROA\UTIL\foxbin2prg
  (git_sync.ps1 + FoxBin2Prg.EXE), care ramane acolo.

  Pasii:
   1. svn update pe ProjectRoot (include externalul COMUN\). Esec / conflicte -> stop, exit nenul.
   2. git_sync.ps1 -ProjectRoot <ProjectRoot> (improspateaza textele .??2, inclusiv COMUN).
      Esec (exit nenul) -> avertizare, fara commit-uri de sync, exit nenul.
   3. Repo proiect (ProjectRoot\.git): pe main comite "sync SVN r<REV>" daca sunt schimbari,
      apoi reconciliaza cu origin si face push conditionat (vezi mai jos).
   4. La fel repo COMUN (ProjectRoot\COMUN\.git), cu revizia din svn info pe folderul COMUN.
   5. Inchide branch-urile claude/* terminate (git diff main..branch gol) in ambele repo-uri.
   6. Sumar final (commit / reconciliere / push per repo + avertismente).

  Reconcilierea cu origin (pasii 3-4, DUPA commit-ul local de sync - commit-ul intai face
  tree-ul curat si aduce sub urmarire fisierele noi, deci nu mai apar blocajele clasice de
  pull peste modificari necomise / fisiere netracked):
   - git fetch origin; fara remote origin sau fetch esuat (offline) -> avertisment si
     comportamentul vechi (doar commit local), exit 0.
   - doar in urma fata de origin/main -> git merge --ff-only origin/main.
   - divergenta -> git rebase origin/main. Commit-urile de sync al caror continut e deja
     identic pe origin (alta masina a rulat acelasi svn update + sync + push inaintea
     noastra) devin goale la rebase si sunt aruncate automat - NU e conflict si nu cere
     interventie manuala.
   - conflict REAL la rebase -> git rebase --abort, avertisment, exit nenul la final.
   - push origin main DOAR daca toate commit-urile ahead de origin/main sunt commit-uri de
     sync ("sync SVN[ r<rev>]" - continut oglindit din SVN, sursa de adevar). Orice commit
     non-sync nepushed blocheaza push-ul automat (avertisment; push manual dupa review).
   - push respins (push simultan de pe alta masina) -> reia fetch + reconciliere + push,
     maxim 3 incercari, apoi avertisment (se reconciliaza la urmatoarea rulare).

  Exit: nenul la esec svn/git_sync sau conflict real cu origin; avertismentele (offline,
  push sarit din cauza commit-urilor non-sync, push esuat) lasa exit 0 si apar in sumar.

.PARAMETER ProjectRoot
  Radacina proiectului. Implicit: directorul curent, daca are .git si un .pjx.

.PARAMETER GitOnly
  Sare peste pasii SVN (1) si regenerarea textelor (2): doar commit + reconciliere + push +
  inchidere branch-uri. Pentru reconciliere rapida cu origin sau pentru teste
  (test_roa_sync.ps1) - nu cere working copy SVN si nici FoxBin2Prg.
#>
[CmdletBinding()]
param(
    [string]$ProjectRoot = '',
    [switch]$GitOnly
)

$ErrorActionPreference = 'Stop'

# --- deduce ProjectRoot din directorul curent daca nu e dat ---
if (-not $ProjectRoot) {
    $cwd = (Get-Location).Path
    $hasGit = Test-Path -LiteralPath (Join-Path $cwd '.git')
    $hasPjx = @(Get-ChildItem -LiteralPath $cwd -Filter '*.pjx' -File -ErrorAction SilentlyContinue).Count -gt 0
    if ($hasGit -and $hasPjx) {
        $ProjectRoot = $cwd
    } else {
        Write-Host "EROARE: -ProjectRoot nedat si directorul curent nu pare radacina de proiect ROA (lipseste .git sau .pjx): $cwd"
        exit 1
    }
}
if (-not (Test-Path -LiteralPath $ProjectRoot -PathType Container)) {
    Write-Host "EROARE: ProjectRoot nu exista: $ProjectRoot"
    exit 1
}
$ProjectRoot = (Get-Item -LiteralPath $ProjectRoot).FullName
$comunRoot   = Join-Path $ProjectRoot 'COMUN'
# tooling-ul de conversie ramane in clona foxbin2prg (acolo sta si FoxBin2Prg.EXE)
$gitSync     = 'D:\ROA\UTIL\foxbin2prg\git_sync.ps1'

Write-Host "=== roa_sync: ProjectRoot=$ProjectRoot$(if($GitOnly){'  (-GitOnly)'}) ==="

# --- helper: revizia SVN a unui folder ---
function Get-SvnRev([string]$path) {
    # EAP local Continue: sub Stop, redirectarea 2>$null a stderr-ului nativ arunca in PS 5.1
    $ErrorActionPreference = 'Continue'
    $rev = (& svn info --show-item revision $path 2>$null | Out-String).Trim()
    if ($LASTEXITCODE -ne 0 -or -not $rev) { return '' }
    return $rev
}

# =========================== 1. svn update ===========================
$rev = ''
Write-Host ''
if ($GitOnly) {
    Write-Host "--- 1. svn update: sarit (-GitOnly) ---"
} else {
    Write-Host "--- 1. svn update ---"
    $svnOut = & svn update $ProjectRoot
    $svnExit = $LASTEXITCODE
    $svnOut | ForEach-Object { Write-Host "  $_" }
    if ($svnExit -ne 0) {
        Write-Host "EROARE: svn update a esuat (exit $svnExit). Ma opresc."
        exit 1
    }
    # svn poate iesi 0 dar raporta conflicte in sumar
    if ($svnOut | Where-Object { $_ -match 'Summary of conflicts|conflict' }) {
        Write-Host "EROARE: svn update raporteaza conflicte. Rezolva-le manual, apoi reia. Ma opresc."
        exit 1
    }
    $rev = Get-SvnRev $ProjectRoot
    Write-Host "  Revizie SVN ProjectRoot: $(if($rev){$rev}else{'necunoscuta'})"
}

# =========================== 2. git_sync ===========================
Write-Host ''
if ($GitOnly) {
    Write-Host "--- 2. git_sync: sarit (-GitOnly) ---"
} else {
    Write-Host "--- 2. git_sync (texte .??2) ---"
    if (-not (Test-Path -LiteralPath $gitSync)) {
        Write-Host "EROARE: nu gasesc git_sync.ps1 la $gitSync. Ma opresc."
        exit 1
    }
    & powershell -ExecutionPolicy Bypass -NoProfile -File $gitSync -ProjectRoot $ProjectRoot
    $syncExit = $LASTEXITCODE
    if ($syncExit -ne 0) {
        Write-Host ''
        Write-Host "AVERTISMENT: git_sync a raportat esecuri (exit $syncExit). NU fac commit-uri de sync. Ma opresc."
        exit 1
    }
}

# =========================== 3-4. commit sync + reconciliere origin per repo ===========================
# Pe main comite schimbarile ca "sync SVN r<rev>", apoi fetch + ff/rebase fata de origin/main
# (sync-urile cu continut deja identic pe origin devin goale la rebase si dispar) si push
# DOAR daca tot ahead-ul e din commit-uri de sync. Intoarce starea pasilor.
function Sync-Repo {
    param([string]$RepoDir, [string]$Rev, [string]$Label)
    # EAP local Continue: git scrie normal pe stderr (fetch/push/rebase), iar sub Stop
    # redirectarea 2>$null ar arunca NativeCommandError in PS 5.1; verificam $LASTEXITCODE.
    $ErrorActionPreference = 'Continue'
    $r = [PSCustomObject]@{ Commit='nimic'; Origin='-'; Push='-'; Warnings=@() }
    if (-not (Test-Path -LiteralPath (Join-Path $RepoDir '.git'))) {
        Write-Host "  ${Label}: fara repo git ($RepoDir) - sar peste."
        $r.Commit = 'fara-repo'
        return $r
    }
    $branch = (& git -C $RepoDir rev-parse --abbrev-ref HEAD).Trim()
    if ($branch -ne 'main') {
        # tree curat = putem trece pe main; altfel nu atingem branch-ul
        $dirty = @(& git -C $RepoDir status --porcelain).Count -gt 0
        if ($dirty) {
            Write-Host "  ${Label}: esti pe '$branch'; sync-ul se face pe main. Ai modificari necomise - sar peste."
            $r.Commit = 'skip-branch'
            return $r
        }
        Write-Host "  ${Label}: pe '$branch' cu tree curat - fac checkout main."
        & git -C $RepoDir checkout main | Out-Null
        if ($LASTEXITCODE -ne 0) {
            Write-Host "  ${Label}: checkout main a esuat - sar peste."
            $r.Commit = 'skip-branch'
            return $r
        }
    }
    & git -C $RepoDir add -A | Out-Null
    $staged = @(& git -C $RepoDir status --porcelain).Count -gt 0
    if ($staged) {
        $msg = if ($Rev) { "sync SVN r$Rev" } else { "sync SVN" }
        & git -C $RepoDir commit -m $msg | Out-Null
        if ($LASTEXITCODE -ne 0) {
            Write-Host "  ${Label}: commit a esuat - sar peste reconciliere."
            $r.Commit = 'esec-commit'
            $r.Warnings += "${Label}: commit de sync esuat"
            return $r
        }
        Write-Host "  ${Label}: commit '$msg'."
        $r.Commit = 'commit'
    } else {
        Write-Host "  ${Label}: nimic de comis."
    }

    # --- reconciliere cu origin (dupa commit: tree curat, fisierele noi sunt deja urmarite) ---
    $originUrl = (& git -C $RepoDir remote get-url origin 2>$null | Out-String).Trim()
    if ($LASTEXITCODE -ne 0 -or -not $originUrl) {
        Write-Host "  ${Label}: fara remote origin - repo doar local, sar peste reconciliere."
        $r.Origin = 'fara-origin'
        return $r
    }
    & git -C $RepoDir fetch origin 2>$null | Out-Null
    if ($LASTEXITCODE -ne 0) {
        Write-Host "  ${Label}: AVERTISMENT: git fetch origin a esuat (offline / gitea inaccesibil?); reconcilierea ramane pe urmatoarea rulare."
        $r.Origin = 'fetch-esuat'
        $r.Warnings += "${Label}: fetch origin esuat - reconciliere amanata"
        return $r
    }
    & git -C $RepoDir rev-parse --verify --quiet origin/main 2>$null | Out-Null
    if ($LASTEXITCODE -ne 0) {
        Write-Host "  ${Label}: origin nu are inca main - sar peste reconciliere."
        $r.Origin = 'fara-origin-main'
        return $r
    }

    for ($attempt = 1; $attempt -le 3; $attempt++) {
        $behind = [int]((& git -C $RepoDir rev-list --count main..origin/main | Out-String).Trim())
        $ahead  = [int]((& git -C $RepoDir rev-list --count origin/main..main | Out-String).Trim())
        if ($behind -gt 0 -and $ahead -eq 0) {
            & git -C $RepoDir merge --ff-only origin/main 2>$null | Out-Null
            if ($LASTEXITCODE -ne 0) {
                Write-Host "  ${Label}: AVERTISMENT: fast-forward la origin/main a esuat neasteptat - verifica manual."
                $r.Origin = 'conflict'
                $r.Warnings += "${Label}: fast-forward esuat - verifica manual repo-ul"
                return $r
            }
            Write-Host "  ${Label}: adus la zi cu origin/main (fast-forward, $behind commit-uri)."
            $r.Origin = 'ff'
        } elseif ($behind -gt 0 -and $ahead -gt 0) {
            Write-Host "  ${Label}: local si origin au divergat (ahead $ahead / behind $behind) - rebase pe origin/main."
            & git -C $RepoDir rebase --empty=drop origin/main 2>$null | Out-Null
            if ($LASTEXITCODE -ne 0) {
                & git -C $RepoDir rebase --abort 2>$null | Out-Null
                Write-Host "  ${Label}: CONFLICT real la rebase pe origin/main - rebase anulat. Rezolva manual: git -C $RepoDir rebase origin/main"
                $r.Origin = 'conflict'
                $r.Warnings += "${Label}: conflict real cu origin/main - reconciliere manuala necesara"
                return $r
            }
            $r.Origin = 'rebase'
        } elseif ($r.Origin -eq '-') {
            $r.Origin = if ($ahead -gt 0) { 'doar-ahead' } else { 'la-zi' }
        }

        # --- push doar daca tot ahead-ul e din commit-uri de sync ---
        $aheadMsgs = @(& git -C $RepoDir log --format=%s origin/main..main) | Where-Object { $_ -and $_.Trim() }
        if ($aheadMsgs.Count -eq 0) {
            Write-Host "  ${Label}: la zi cu origin/main; nimic de impins."
            $r.Push = 'nimic'
            return $r
        }
        $nonSync = @($aheadMsgs | Where-Object { $_ -notmatch '^sync SVN( r\S+)?$' })
        if ($nonSync.Count -gt 0) {
            Write-Host "  ${Label}: AVERTISMENT: ahead de origin include commit-uri non-sync - NU fac push automat (push manual dupa review):"
            $nonSync | Select-Object -First 5 | ForEach-Object { Write-Host "    - $_" }
            $r.Push = 'sarit-non-sync'
            $r.Warnings += "${Label}: push sarit - exista commit-uri non-sync nepushed"
            return $r
        }
        & git -C $RepoDir push origin main 2>$null | Out-Null
        if ($LASTEXITCODE -eq 0) {
            Write-Host "  ${Label}: push origin main ($($aheadMsgs.Count) commit-uri de sync)."
            $r.Push = 'push'
            return $r
        }
        if ($attempt -lt 3) {
            Write-Host "  ${Label}: push respins (probabil push simultan de pe alta masina) - refetch si reiau (incercarea $($attempt+1))."
            & git -C $RepoDir fetch origin 2>$null | Out-Null
            if ($LASTEXITCODE -ne 0) {
                Write-Host "  ${Label}: AVERTISMENT: refetch esuat dupa push respins."
                $r.Push = 'esec'
                $r.Warnings += "${Label}: push esuat si refetch esuat - reconciliere la urmatoarea rulare"
                return $r
            }
        }
    }
    Write-Host "  ${Label}: AVERTISMENT: push respins de 3 ori - renunt; se reconciliaza la urmatoarea rulare."
    $r.Push = 'esec'
    $r.Warnings += "${Label}: push esuat dupa 3 incercari"
    return $r
}

Write-Host ''
Write-Host "--- 3. sync repo proiect ---"
$resProj = Sync-Repo -RepoDir $ProjectRoot -Rev $rev -Label 'proiect'

Write-Host ''
Write-Host "--- 4. sync repo COMUN ---"
$revComun = ''
$resComun = [PSCustomObject]@{ Commit='nimic'; Origin='-'; Push='-'; Warnings=@() }
if (Test-Path -LiteralPath $comunRoot -PathType Container) {
    if (-not $GitOnly) { $revComun = Get-SvnRev $comunRoot }
    $resComun = Sync-Repo -RepoDir $comunRoot -Rev $revComun -Label 'COMUN'
} else {
    Write-Host "  COMUN: folderul nu exista ($comunRoot) - sar peste."
}

# =========================== 5. inchidere branch-uri claude/* ===========================
# Inchide branch-urile claude/* al caror diff fata de main e gol (munca lor a intrat prin sync).
function Close-ClaudeBranches {
    param([string]$RepoDir, [string]$Label)
    $closed = @(); $active = @()
    if (-not (Test-Path -LiteralPath (Join-Path $RepoDir '.git'))) { return @{ Closed=$closed; Active=$active } }
    $branches = @(& git -C $RepoDir for-each-ref --format='%(refname:short)' refs/heads/claude 2>$null) |
        Where-Object { $_ -and $_.Trim() } | ForEach-Object { $_.Trim() }
    foreach ($b in $branches) {
        $diff = @(& git -C $RepoDir diff --name-only main..$b 2>$null) | Where-Object { $_ -and $_.Trim() }
        if ($diff.Count -eq 0) {
            & git -C $RepoDir branch -D $b | Out-Null
            if ($LASTEXITCODE -eq 0) { $closed += $b; Write-Host "  ${Label}: inchis $b" }
            else { Write-Host "  ${Label}: nu am putut sterge $b" }
        } else {
            $active += $b
            Write-Host "  ${Label}: activ $b ($($diff.Count) fisiere diferite)"
        }
    }
    return @{ Closed=$closed; Active=$active }
}

Write-Host ''
Write-Host "--- 5. inchidere branch-uri claude/* ---"
$brProj  = Close-ClaudeBranches -RepoDir $ProjectRoot -Label 'proiect'
$brComun = @{ Closed=@(); Active=@() }
if (Test-Path -LiteralPath $comunRoot -PathType Container) {
    $brComun = Close-ClaudeBranches -RepoDir $comunRoot -Label 'COMUN'
}

# =========================== 6. sumar ===========================
$commits = @()
if ($resProj.Commit  -eq 'commit') { $commits += "proiect: sync SVN$(if($rev){" r$rev"})" }
if ($resComun.Commit -eq 'commit') { $commits += "COMUN: sync SVN$(if($revComun){" r$revComun"})" }
$closedAll = @($brProj.Closed + $brComun.Closed)
$activeAll = @($brProj.Active + $brComun.Active)
$warnAll   = @($resProj.Warnings + $resComun.Warnings)

Write-Host ''
Write-Host "===================== SUMAR ====================="
Write-Host "Revizie SVN: proiect=$(if($rev){$rev}else{'?'})$(if($revComun){"  COMUN=$revComun"})"
if ($commits.Count -gt 0) {
    Write-Host "Commit-uri de sync:"
    $commits | ForEach-Object { Write-Host "  $_" }
} else {
    Write-Host "Commit-uri de sync: nimic de sincronizat"
}
Write-Host "Origin: proiect=$($resProj.Origin)/push=$($resProj.Push)  COMUN=$($resComun.Origin)/push=$($resComun.Push)"
Write-Host "Branch-uri inchise: $(if($closedAll.Count){$closedAll -join ', '}else{'niciunul'})"
Write-Host "Branch-uri active:  $(if($activeAll.Count){$activeAll -join ', '}else{'niciunul'})"
if ($warnAll.Count -gt 0) {
    Write-Host "AVERTISMENTE:"
    $warnAll | ForEach-Object { Write-Host "  $_" }
}
Write-Host "================================================"
if (($resProj.Origin -eq 'conflict') -or ($resComun.Origin -eq 'conflict')) { exit 1 }
exit 0
