name: Build Multi-Platform

on:
  push:
    branches: [ master, main ]
    tags:
      - 'v*.*.*'
  pull_request:
    branches: [ master, main ]
    types: [labeled]
  workflow_dispatch:

jobs:
  test:
    if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v6

    - name: Setup .NET
      uses: actions/setup-dotnet@v5
      with:
        dotnet-version: '10.0.x'

    - name: Restore dependencies
      run: dotnet restore dotcraft.sln

    - name: Run tests
      run: dotnet test dotcraft.sln --configuration Release --no-restore

  build:
    needs: test
    if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'build')
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-15-intel]
        include:
          - os: ubuntu-latest
            os_name: linux
            runtime_id: linux-x64
            output_name: DotCraft-linux-x64
            cargo_target: x86_64-unknown-linux-gnu
          - os: windows-latest
            os_name: windows
            runtime_id: win-x64
            output_name: DotCraft-win-x64
            cargo_target: x86_64-pc-windows-msvc
          - os: macos-15-intel
            os_name: macos
            runtime_id: osx-x64
            output_name: DotCraft-macos-x64
            cargo_target: x86_64-apple-darwin

    runs-on: ${{ matrix.os }}

    steps:
    - name: Checkout code
      uses: actions/checkout@v6

    - name: Setup .NET
      uses: actions/setup-dotnet@v5
      with:
        dotnet-version: '10.0.x'

    - name: Restore dependencies
      run: dotnet restore src/DotCraft.App/DotCraft.App.csproj

    - name: Setup Rust
      uses: dtolnay/rust-toolchain@stable

    - name: Cache Rust dependencies
      uses: Swatinem/rust-cache@v2
      with:
        workspaces: tui

    - name: Build ${{ matrix.os_name }}
      run: dotnet publish src/DotCraft.App/DotCraft.App.csproj -c Release -r ${{ matrix.runtime_id }} -p:PublishProfile=ReleaseProfile -o ./publish/${{ matrix.output_name }}

    - name: Build TUI (dotcraft-tui)
      run: |
        cargo build --release --target ${{ matrix.cargo_target }}
      working-directory: tui

    - name: Copy TUI binary to publish - Windows
      if: runner.os == 'Windows'
      run: Copy-Item -Path tui\target\${{ matrix.cargo_target }}\release\dotcraft-tui.exe -Destination publish\${{ matrix.output_name }}\

    - name: Copy TUI binary to publish - Linux/macOS
      if: runner.os != 'Windows'
      run: cp tui/target/${{ matrix.cargo_target }}/release/dotcraft-tui publish/${{ matrix.output_name }}/

    - name: Create archive for Linux/macOS
      if: runner.os != 'Windows'
      run: |
        cd publish/${{ matrix.output_name }}
        tar -czf ../../${{ matrix.output_name }}.tar.gz *
        cd ../..

    - name: Create archive for Windows
      if: runner.os == 'Windows'
      run: |
        Compress-Archive -Path publish/${{ matrix.output_name }}/* -DestinationPath ${{ matrix.output_name }}.zip

    - name: Upload artifact - Linux/macOS
      if: runner.os != 'Windows'
      uses: actions/upload-artifact@v6
      with:
        name: ${{ matrix.output_name }}
        path: ${{ matrix.output_name }}.tar.gz

    - name: Upload artifact - Windows
      if: runner.os == 'Windows'
      uses: actions/upload-artifact@v6
      with:
        name: ${{ matrix.output_name }}
        path: ${{ matrix.output_name }}.zip

    # ── Desktop (Electron) build ──────────────────────────────────────────────

    - name: Setup Node.js
      if: runner.os != 'Linux'
      uses: actions/setup-node@v6
      with:
        node-version: '22'
        cache: 'npm'
        cache-dependency-path: |
          desktop/package-lock.json
          sdk/typescript/package-lock.json

    - name: Install desktop dependencies
      if: runner.os != 'Linux'
      run: npm ci
      working-directory: desktop

    - name: Download bundled CLIProxyAPI for desktop
      if: runner.os != 'Linux'
      run: npm run download:cliproxyapi
      working-directory: desktop

    - name: Install TypeScript SDK dependencies
      if: runner.os != 'Linux'
      run: npm ci
      working-directory: sdk/typescript

    - name: Build TypeScript SDK
      if: runner.os != 'Linux'
      run: npm run build:all
      working-directory: sdk/typescript

    - name: Stage embedded binary for desktop - Windows
      if: runner.os == 'Windows'
      shell: pwsh
      run: |
        New-Item -ItemType Directory -Force -Path desktop/resources/bin | Out-Null
        Copy-Item -Path publish/${{ matrix.output_name }}/dotcraft.exe -Destination desktop/resources/bin/dotcraft.exe -Force

    - name: Stage embedded binary for desktop - macOS
      if: runner.os == 'macOS'
      shell: bash
      run: |
        mkdir -p desktop/resources/bin
        cp publish/${{ matrix.output_name }}/dotcraft desktop/resources/bin/dotcraft

    - name: Stage bundled modules for desktop - Windows
      if: runner.os == 'Windows'
      shell: pwsh
      run: |
        $modules = @('channel-feishu', 'channel-qq', 'channel-wecom', 'channel-weixin', 'channel-telegram')
        foreach ($mod in $modules) {
          $src = "sdk/typescript/packages/$mod"
          $dst = "desktop/resources/modules/$mod"
          New-Item -ItemType Directory -Force -Path $dst | Out-Null
          Copy-Item -Path "$src/manifest.json" -Destination "$dst/manifest.json" -Force
          Copy-Item -Path "$src/package.json" -Destination "$dst/package.json" -Force
          Copy-Item -Path "$src/dist" -Destination "$dst/dist" -Recurse -Force
        }

    - name: Stage bundled modules for desktop - macOS
      if: runner.os == 'macOS'
      shell: bash
      run: |
        for mod in channel-feishu channel-qq channel-wecom channel-weixin channel-telegram; do
          src="sdk/typescript/packages/$mod"
          dst="desktop/resources/modules/$mod"
          mkdir -p "$dst"
          cp "$src/manifest.json" "$dst/manifest.json"
          cp "$src/package.json" "$dst/package.json"
          cp -r "$src/dist" "$dst/dist"
        done

    # Match build.bat: avoid stale dist confusing electron-builder; safe on fresh checkouts too.
    - name: Clean desktop dist
      if: runner.os != 'Linux'
      shell: bash
      run: rm -rf desktop/dist

    - name: Build desktop app
      if: runner.os != 'Linux'
      run: |
        npm run download:cliproxyapi
        npm run build
        npx electron-builder --publish never
        npm run verify:package
      working-directory: desktop
      env:
        # No code signing cert in CI; skip discovery so macOS/Windows builds do not fail.
        CSC_IDENTITY_AUTO_DISCOVERY: 'false'

    - name: Prepare Windows artifacts
      if: runner.os == 'Windows'
      shell: bash
      run: |
        mkdir -p desktop/dist/installer
        mv desktop/dist/*Setup*.exe desktop/dist/installer/ || true

    - name: Upload app installer - Windows
      if: runner.os == 'Windows'
      uses: actions/upload-artifact@v6
      with:
        name: DotCraft-Installer-win-x64
        path: desktop/dist/installer/*.exe

    - name: Upload app artifacts - macOS
      if: runner.os == 'macOS'
      uses: actions/upload-artifact@v6
      with:
        name: DotCraft-macos-x64-dmg
        path: desktop/dist/*.dmg

  release:
    needs: build
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
    - name: Checkout code
      uses: actions/checkout@v6

    - name: Download all artifacts
      uses: actions/download-artifact@v7
      with:
        path: artifacts

    - name: Rename artifacts with version
      run: |
        VERSION=${GITHUB_REF#refs/tags/}
        echo "Version: $VERSION"
        
        # Create release directory
        mkdir -p release
        
        # Rename CLI artifacts
        for file in artifacts/DotCraft-*/DotCraft-*.tar.gz; do
          [ -f "$file" ] || continue
          # Extract platform from filename (e.g., DotCraft-linux-x64.tar.gz -> linux)
          platform=$(basename "$file" | sed 's/DotCraft-\(.*\)-x64.tar.gz/\1/')
          new_name="DotCraft-${VERSION}-${platform}-x64.tar.gz"
          cp "$file" "release/$new_name"
          echo "Renamed: $(basename $file) -> $new_name"
        done
        
        for file in artifacts/DotCraft-*/DotCraft-*.zip; do
          [ -f "$file" ] || continue
          platform=$(basename "$file" | sed 's/DotCraft-\(.*\)-x64.zip/\1/')
          new_name="DotCraft-${VERSION}-${platform}-x64.zip"
          cp "$file" "release/$new_name"
          echo "Renamed: $(basename $file) -> $new_name"
        done
        
        # Rename app artifacts - Windows
        for file in artifacts/DotCraft-Installer-win-x64/*.exe; do
          [ -f "$file" ] || continue
          new_name="DotCraft-${VERSION}-win-x64-Setup.exe"
          cp "$file" "release/$new_name"
          echo "Renamed: $(basename $file) -> $new_name"
        done

        # Rename app artifacts - macOS
        for file in artifacts/DotCraft-macos-x64-dmg/*.dmg; do
          [ -f "$file" ] || continue
          new_name="DotCraft-${VERSION}-macos-x64.dmg"
          cp "$file" "release/$new_name"
          echo "Renamed: $(basename $file) -> $new_name"
        done
        
        echo "Final release files:"
        ls -lh release/

    - name: Prepare Release Notes
      id: release_notes
      run: |
        VERSION=${GITHUB_REF#refs/tags/}
        REPO=${{ github.repository }}

        # Use template file when available.
        if [ -f ".github/release_template.md" ]; then
          sed \
            -e "s|{{ VERSION }}|$VERSION|g" \
            -e "s|{{ REPO }}|$REPO|g" \
            -e "s|{{ CHANGELOG }}|- See auto-generated notes below.|g" \
            ".github/release_template.md" > "release_notes.md"
        else
          echo "Automatic release $VERSION" > "release_notes.md"
        fi

    - name: Create Release
      uses: softprops/action-gh-release@v2
      with:
        files: release/*
        body_path: release_notes.md
        append_body: true
        draft: false
        prerelease: false
        generate_release_notes: true
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
