# Gitea Actions CI/CD - build + deploy SOLUTION_ERP to IIS on same VPS.
# Trigger: push to main, or manual dispatch.
#
# Self-hosted Windows runner on VPS (shared with VIETREPORT). Runner has:
#   - git, .NET 10 SDK, Node 20, IIS
#   - Can deploy locally (no WinRM needed)
#
# Secrets required in Gitea repo settings:
#   - JWT_SECRET     (64+ chars random)
#   - DB_CONNECTION  (full connection string with vrapp password)

name: Deploy SOLUTION_ERP

# Path filter — skip CI khi commit chỉ docs/MD/skill (~110s saved per docs commit).
# Commit MD-only như "Docs: chốt session" sẽ KHÔNG trigger workflow.
# Lưu ý: nếu cùng 1 commit thay đổi cả MD + code → vẫn trigger (đúng behavior).
on:
  push:
    branches: [main]
    paths-ignore:
      - 'docs/**'
      - '**/*.md'
      - '.claude/skills/**'
      - '.gitignore'
      - 'scripts/**.md'
  workflow_dispatch:

jobs:
  build-deploy:
    runs-on: windows-latest
    steps:
      # Manual checkout thay vì `uses: actions/checkout@v4` — tránh phụ thuộc
      # github.com (act_runner mỗi run đều `git fetch` để check update action,
      # khi VPS → github.com TCP timeout 21s thì toàn job fail trước khi tới
      # test gate). Gitea internal network luôn ổn định, nên clone trực tiếp.
      # Token `${{ github.token }}` (Gitea cũng dùng tên này) tự sẵn cho job.
      - name: Checkout (manual git, bypass github.com)
        shell: powershell
        run: |
          git config --global --add safe.directory '*'
          git init -q
          git remote add origin "https://gitea-actions:${{ github.token }}@git.baocaogiaoduc.vn/${{ github.repository }}.git"
          # Fetch ref (branch) thay vì SHA — không cần Gitea allow SHA fetch.
          # Depth 30 đủ buffer nếu main đã commit thêm sau khi job pickup.
          $ref = "${{ github.ref }}"
          if ($ref -like "refs/heads/*") { $ref = $ref.Substring(11) }
          git fetch --depth=30 origin $ref
          git checkout --quiet "${{ github.sha }}"
          git log -1 --oneline

      - name: Show tool versions
        shell: powershell
        run: |
          & 'C:\Program Files\dotnet\dotnet.exe' --version
          & 'C:\Program Files\nodejs\node.exe' --version
          & 'C:\Program Files\nodejs\npm.cmd' --version

      # ============== TEST GATE ==============
      # Run tests TRƯỚC build/publish/deploy. Fail → exit non-zero → no deploy.
      # Phase 1: Domain (54 test policy state machine).
      # Phase 2: Infrastructure (17 test code generators format/sequence/year scope).
      - name: Run unit tests (Domain)
        shell: powershell
        run: |
          & 'C:\Program Files\dotnet\dotnet.exe' test tests/SolutionErp.Domain.Tests/SolutionErp.Domain.Tests.csproj `
            --configuration Release `
            --logger "trx;LogFileName=domain-tests.trx" `
            --results-directory test-results
          if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

      - name: Run integration tests (Infrastructure - SQLite in-memory)
        shell: powershell
        run: |
          & 'C:\Program Files\dotnet\dotnet.exe' test tests/SolutionErp.Infrastructure.Tests/SolutionErp.Infrastructure.Tests.csproj `
            --configuration Release `
            --logger "trx;LogFileName=infra-tests.trx" `
            --results-directory test-results
          if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

      # Upload test results — bỏ vì `actions/upload-artifact@v4` cũng phụ thuộc
      # github.com fetch (cùng vấn đề như actions/checkout). TRX file vẫn save
      # local trong workspace `test-results/` cho debug khi cần.
      - name: List test results (local debug)
        if: always()
        shell: powershell
        run: |
          if (Test-Path test-results) {
            Get-ChildItem test-results -Recurse | Format-Table FullName, Length
          } else {
            Write-Host "No test-results directory."
          }

      # ============== BUILD ==============
      - name: Build backend
        shell: powershell
        run: |
          & 'C:\Program Files\dotnet\dotnet.exe' restore SolutionErp.slnx
          & 'C:\Program Files\dotnet\dotnet.exe' publish src/Backend/SolutionErp.Api/SolutionErp.Api.csproj `
            --configuration Release `
            --output out/api `
            --runtime win-x64 `
            --self-contained false

      # FE build — npm install fresh mỗi run (Vite 8 rolldown gotcha #20).
      # NOTE: Junction-based npm cache đã thử ở commit 29eb5d9 nhưng FAIL — `tsc not found`,
      # script không hiện Write-Host log. Cần debug riêng (xem gotcha #39 mới).
      # Tạm dùng fresh install như cũ (49s + 33s = 82s/run). Path filter ở trên đã
      # save 100% time cho commit MD-only — đó là win lớn nhất.
      - name: Build fe-admin
        shell: powershell
        working-directory: fe-admin
        run: |
          # Vite 8 rolldown native binding must match platform; fresh resolve on Windows
          Remove-Item node_modules, package-lock.json -Recurse -Force -ErrorAction SilentlyContinue
          & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund
          & 'C:\Program Files\nodejs\npm.cmd' run build

      - name: Build fe-user
        shell: powershell
        working-directory: fe-user
        run: |
          Remove-Item node_modules, package-lock.json -Recurse -Force -ErrorAction SilentlyContinue
          & 'C:\Program Files\nodejs\npm.cmd' install --no-audit --no-fund
          & 'C:\Program Files\nodejs\npm.cmd' run build

      - name: Deploy to IIS (local)
        if: github.ref == 'refs/heads/main'
        shell: powershell
        env:
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          DB_CONNECTION: ${{ secrets.DB_CONNECTION }}
        run: |
          Import-Module WebAdministration

          # Stop app pool (if running) so DLLs are writable
          $poolState = (Get-WebAppPoolState -Name SolutionErp-Api -ErrorAction SilentlyContinue).Value
          if ($poolState -eq 'Started') {
              Stop-WebAppPool -Name SolutionErp-Api
              Start-Sleep -Seconds 3
          }

          # Deploy API
          Remove-Item -Path 'C:\inetpub\solution-erp\api\*' -Recurse -Force -Exclude 'appsettings.Production.json','logs','uploads','wwwroot' -ErrorAction SilentlyContinue
          Copy-Item -Path 'out\api\*' -Destination 'C:\inetpub\solution-erp\api\' -Recurse -Force

          # Write appsettings.Production.json from source template + secrets.
          # Template is in source workspace (not in publish output - dotnet publish
          # doesn't copy .example files).
          $example = 'src\Backend\SolutionErp.Api\appsettings.Production.json.example'
          $prod    = 'C:\inetpub\solution-erp\api\appsettings.Production.json'
          $settings = Get-Content $example -Raw | ConvertFrom-Json
          $settings.ConnectionStrings.Default = $env:DB_CONNECTION
          $settings.Jwt.Secret = $env:JWT_SECRET
          $settings | ConvertTo-Json -Depth 10 | Set-Content -Path $prod -Encoding UTF8
          Write-Host "Wrote appsettings.Production.json"

          # Restrict ACL
          icacls $prod /inheritance:r | Out-Null
          icacls $prod /grant:r 'Administrators:(R,W)' 'IIS AppPool\SolutionErp-Api:(R)' | Out-Null

          # Deploy fe-admin
          Remove-Item -Path 'C:\inetpub\solution-erp\fe-admin\*' -Recurse -Force -Exclude 'web.config' -ErrorAction SilentlyContinue
          Copy-Item -Path 'fe-admin\dist\*' -Destination 'C:\inetpub\solution-erp\fe-admin\' -Recurse -Force

          # Deploy fe-user
          Remove-Item -Path 'C:\inetpub\solution-erp\fe-user\*' -Recurse -Force -Exclude 'web.config' -ErrorAction SilentlyContinue
          Copy-Item -Path 'fe-user\dist\*' -Destination 'C:\inetpub\solution-erp\fe-user\' -Recurse -Force

          # Restart app pool
          Start-WebAppPool -Name SolutionErp-Api
          Write-Host "Deploy done. App pool started."

      - name: Smoke test
        if: github.ref == 'refs/heads/main'
        shell: powershell
        run: |
          Start-Sleep -Seconds 10
          try {
              $r = Invoke-WebRequest -Uri 'https://api.solutions.com.vn/health/live' -TimeoutSec 30 -UseBasicParsing
              Write-Host "API /health/live -> $($r.StatusCode)"
          } catch {
              Write-Warning "API smoke test: $_"
          }
