name: Build, Push, Publish

on:
  push:
    branches:
      - main
  workflow_dispatch:
  schedule:
    - cron: "28 5 * * *"
  # workflow_run support in Gitea can be tricky, keeping it but might need adjustment
  workflow_run:
    workflows: ["Sync Repo"]
    types:
      - completed

jobs:
  release:
    name: Build & Release
    runs-on: ubuntu-latest
    container:
      image: catthehacker/ubuntu:act-latest

    permissions:
      contents: write
      packages: write

    steps:
      - name: 📥 Checkout code with full history and tags
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check for Dockerargs and Upstream Updates
        id: check_upstream
        run: |
          if [ -f Dockerargs ]; then
            echo "Dockerargs found. Checking upstream..."
            echo "Dockerargs found. Checking upstream..."
            # Parse repo info using awk to avoid git config restrictions on underscores in keys elsewhere in the file
            REPO_URL=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^url=/{print $2}' Dockerargs | tr -d ' \r\n')
            REPO_BRANCH=$(awk -F '=' '/\[repo\]/{flag=1; next} /\[/{flag=0} flag && /^branch=/{print $2}' Dockerargs | tr -d ' \r\n')
            if [ -z "$REPO_BRANCH" ]; then REPO_BRANCH="main"; fi
            
            # Fetch upstream SHA
            if [ -n "$REPO_URL" ]; then
              UPSTREAM_SHA=$(git ls-remote "$REPO_URL" "$REPO_BRANCH" | awk '{ print $1 }' | head -c 7)
              echo "Upstream SHA: $UPSTREAM_SHA"
              
              if [ -f manifest.json ]; then
                LOCAL_SHA=$(jq -r '.upstream_sha // empty' manifest.json)
              else
                LOCAL_SHA=""
              fi
              
              if [ "$LOCAL_SHA" != "$UPSTREAM_SHA" ]; then
                echo "Upstream changed ($LOCAL_SHA -> $UPSTREAM_SHA)."
                echo "upstream_needs_update=true" >> "$GITHUB_OUTPUT"
                echo "upstream_sha=$UPSTREAM_SHA" >> "$GITHUB_OUTPUT"
                echo "repo_url=$REPO_URL" >> "$GITHUB_OUTPUT"
                echo "repo_branch=$REPO_BRANCH" >> "$GITHUB_OUTPUT"
              else
                echo "Upstream up to date."
                echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
              fi
              
              # Parse Build Args
              echo "Parsing [args] from Dockerargs..."
              ARGS_CONTENT=$(sed -n '/^\[args\]/,/^\[/p' Dockerargs | grep -v '^\[' | grep '=' || true)
              if [ -n "$ARGS_CONTENT" ]; then
                 echo "Found args:"
                 echo "$ARGS_CONTENT"
                 echo "build_args<<EOF" >> "$GITHUB_OUTPUT"
                 echo "$ARGS_CONTENT" >> "$GITHUB_OUTPUT"
                 echo "EOF" >> "$GITHUB_OUTPUT"
              else
                 echo "No args found."
                 echo "build_args=" >> "$GITHUB_OUTPUT"
              fi
            else
               echo "Repo URL not found in Dockerargs."
               echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
               echo "build_args=" >> "$GITHUB_OUTPUT"
            fi
          else
            echo "No Dockerargs found."
            echo "upstream_needs_update=false" >> "$GITHUB_OUTPUT"
            echo "build_args=" >> "$GITHUB_OUTPUT"
          fi

      - name: Check if any tags exist
        id: check_tags_exist
        run: |
          git fetch --tags
          TAG_COUNT=$(git tag | wc -l)
          if [ "$TAG_COUNT" -eq 0 ]; then
            echo "has_tags=false" >> "$GITHUB_OUTPUT"
            echo "latest_tag=v0.0.0" >> "$GITHUB_OUTPUT"
          else
            echo "has_tags=true" >> "$GITHUB_OUTPUT"
            LATEST_TAG=$(git describe --tags --abbrev=0)
            echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
          fi

      - name: Check if meaningful commits exist since latest tag
        id: check_commits
        run: |
          UPSTREAM_UPDATE="${{ steps.check_upstream.outputs.upstream_needs_update }}"
          if [ "$UPSTREAM_UPDATE" == "true" ]; then
            echo "commit_count=1" >> "$GITHUB_OUTPUT"
            echo "changed_files=Upstream Update to ${{ steps.check_upstream.outputs.upstream_sha }}" >> "$GITHUB_OUTPUT"
          elif [ "${{ steps.check_tags_exist.outputs.has_tags }}" = "false" ]; then
            # No tags exist, so we should create first release
            echo "commit_count=1" >> "$GITHUB_OUTPUT"
            CHANGED_FILES=$(git ls-files | grep -v '^manifest.json$' || true)
            if [ -n "$CHANGED_FILES" ]; then
              echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
              printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
              echo "EOF" >> "$GITHUB_OUTPUT"
            else
              echo "changed_files=Initial release" >> "$GITHUB_OUTPUT"
            fi
          else
            LATEST_TAG="${{ steps.check_tags_exist.outputs.latest_tag }}"
            CHANGED_FILES="$(git diff --name-only "${LATEST_TAG}..HEAD" | grep -v '^manifest.json$' || true)"
            if [ -n "$CHANGED_FILES" ]; then
              echo "commit_count=1" >> "$GITHUB_OUTPUT"
              echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
              printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
              echo "EOF" >> "$GITHUB_OUTPUT"
            else
              echo "commit_count=0" >> "$GITHUB_OUTPUT"
            fi
          fi

      - name: Get latest release tag (from Gitea API)
        id: get_latest_release
        run: |
          # Using Gitea API
          LATEST_RELEASE_TAG=$(curl -sL -H "Accept: application/json" \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/latest" | jq -r .tag_name)

          if [ -z "$LATEST_RELEASE_TAG" ] || [ "$LATEST_RELEASE_TAG" = "null" ]; then
            LATEST_RELEASE_TAG="v1.0.0"
          fi
          echo "latest_release_tag=$LATEST_RELEASE_TAG" >> "$GITHUB_OUTPUT"
          echo "latest_release_version=${LATEST_RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"

      # -------------------------------
      # Sync manifest.json to last release version if behind (only when no meaningful commits)
      # -------------------------------
      - name: 🛠 Ensure manifest.json matches latest release version
        if: steps.check_commits.outputs.commit_count == '0'
        run: |
          if [ -f manifest.json ]; then
            MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
          else
            MANIFEST_VERSION=""
          fi
          LATEST_RELEASE_VERSION="${{ steps.get_latest_release.outputs.latest_release_version }}"
          PYTHON_CODE="from packaging import version; \
            print(version.parse('$LATEST_RELEASE_VERSION') > version.parse('$MANIFEST_VERSION') if '$MANIFEST_VERSION' else True)"
          # Python3 is available in catthehacker/ubuntu:act-latest
          NEED_UPDATE=$(python3 -c "$PYTHON_CODE")
          if [ "$NEED_UPDATE" = "True" ]; then
            echo "Updating manifest.json to version $LATEST_RELEASE_VERSION (sync with release)"
            jq --arg v "$LATEST_RELEASE_VERSION" '.version = $v' manifest.json > tmp.json && mv tmp.json manifest.json
            git config user.name "Gitea Actions"
            git config user.email "actions@git.icc.gg"
            git add manifest.json
            git commit -m "Sync manifest.json to release $LATEST_RELEASE_VERSION [🔄]" || echo "Nothing to commit"
            git push origin main || true
          else
            echo "Manifest.json is already up-to-date with the latest release."
          fi

      # -------------------------------
      # Continue normal workflow if commits exist
      # -------------------------------
      - name: 📃 Get list of changed files (Markdown bullet list)
        if: steps.check_commits.outputs.commit_count != '0'
        id: changed_files
        run: |
          BULLET_LIST="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | sed 's/^/- /')"
          echo "CHANGED<<EOF" >> "$GITHUB_OUTPUT"
          printf '%s\n' "$BULLET_LIST" >> "$GITHUB_OUTPUT"
          echo "EOF" >> "$GITHUB_OUTPUT"
          COUNT="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | wc -l)"
          echo "COUNT=$COUNT" >> "$GITHUB_OUTPUT"

      - name: Get manifest version
        if: steps.check_commits.outputs.commit_count != '0'
        id: get_manifest_version
        run: |
          if [ -f manifest.json ]; then
            MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
            if [ -z "$MANIFEST_VERSION" ] || [ "$MANIFEST_VERSION" = "null" ]; then
              MANIFEST_VERSION="1.0.0"
            fi
          else
            MANIFEST_VERSION="1.0.0"
          fi
          echo "manifest_version=$MANIFEST_VERSION" >> "$GITHUB_OUTPUT"

      - name: Pick base version
        if: steps.check_commits.outputs.commit_count != '0'
        id: pick_base_version
        run: |
          LATEST_RELEASE="${{ steps.get_latest_release.outputs.latest_release_version }}"
          MANIFEST="${{ steps.get_manifest_version.outputs.manifest_version }}"
          BASE_VERSION=$(python3 -c "from packaging import version; \
            print(str(max(version.parse('$LATEST_RELEASE'), version.parse('$MANIFEST'))))")
          echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"

      - name: 🔢 Determine version
        if: steps.check_commits.outputs.commit_count != '0'
        id: version
        run: |
          BASE_VERSION="${{ steps.pick_base_version.outputs.base_version }}"
          MAJOR=$(echo "$BASE_VERSION" | cut -d. -f1)
          MINOR=$(echo "$BASE_VERSION" | cut -d. -f2)
          PATCH=$(echo "$BASE_VERSION" | cut -d. -f3)
          COUNT="${{ steps.changed_files.outputs.COUNT }}"
          if [ "$COUNT" -ge 5 ]; then
            MAJOR=$((MAJOR + 1))
            MINOR=0
            PATCH=0
          elif [ "$COUNT" -ge 3 ]; then
            MINOR=$((MINOR + 1))
            PATCH=0
          else
            PATCH=$((PATCH + 1))
          fi
          NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
          REPO_NAME="$(basename "$GITHUB_REPOSITORY")"
          ZIP_NAME="${REPO_NAME}-${NEW_VERSION}.zip"
          echo "VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT"
          echo "ZIP_NAME=$ZIP_NAME" >> "$GITHUB_OUTPUT"
          echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_OUTPUT"

      - name: 🛠 Update or create manifest.json
        if: steps.check_commits.outputs.commit_count != '0'
        run: |
          VERSION="${{ steps.version.outputs.VERSION }}"
          AUTHOR="Ivan Carlos"
          VERSION_FILE="manifest.json"
          UPSTREAM_SHA="${{ steps.check_upstream.outputs.upstream_sha }}"

          if [ -f "$VERSION_FILE" ]; then
             jq --arg v "$VERSION" \
                --arg a "$AUTHOR" \
                --arg u "$UPSTREAM_SHA" \
                '.version = $v | .author = $a | if $u != "" and $u != null then .upstream_sha = $u else . end' \
                "$VERSION_FILE" > tmp.json && mv tmp.json "$VERSION_FILE"
          else
            echo "{ \"version\": \"$VERSION\", \"author\": \"$AUTHOR\", \"upstream_sha\": \"$UPSTREAM_SHA\" }" > "$VERSION_FILE"
          fi

      - name: 💾 Commit and push updated manifest.json
        if: steps.check_commits.outputs.commit_count != '0'
        run: |
          git config user.name "Gitea Actions"
          git config user.email "actions@git.icc.gg"
          git add manifest.json
          git commit -m "Update manifest version to ${{ steps.version.outputs.VERSION }} [▶️]" || echo "Nothing to commit"
          git push origin main

      - name: 🛠 Install zip
        if: steps.check_commits.outputs.commit_count != '0'
        run: |
          apt-get update && apt-get install -y zip

      - name: 📦 Create ZIP package (excluding certain files)
        if: steps.check_commits.outputs.commit_count != '0'
        run: |
          ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
          zip -r "$ZIP_NAME" . -x ".git/*" ".github/*" "docker/*" ".dockerignore" "CNAME" "Dockerfile" "README.md" "LICENSE" ".gitea/*"

      - name: 🚀 Create Gitea Release
        if: steps.check_commits.outputs.commit_count != '0'
        id: create_release
        env:
          CHANGELOG_LIST: ${{ steps.changed_files.outputs.CHANGED }}
        run: |
          TAG_NAME="v${{ steps.version.outputs.VERSION }}"
          RELEASE_NAME="${{ steps.version.outputs.REPO_NAME }} v${{ steps.version.outputs.VERSION }}"

          # Construct Markdown body safely using env var
          # We use printf to avoid interpreting backslashes in the file list
          BODY=$(printf "### Changelog\nFiles changed in this release:\n%s" "$CHANGELOG_LIST")

          # Create JSON payload using jq
          jq -n \
            --arg tag_name "$TAG_NAME" \
            --arg name "$RELEASE_NAME" \
            --arg body "$BODY" \
            '{tag_name: $tag_name, name: $name, body: $body, draft: false, prerelease: false}' > release_payload.json

          echo "DEBUG: Generated Payload:"
          cat release_payload.json

          # Create Release
          curl -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases" \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Content-Type: application/json" \
            -d @release_payload.json > api_response.json

          echo "DEBUG: API Response:"
          cat api_response.json || true

          RELEASE_ID=$(jq -r .id api_response.json)
          echo "RELEASE_ID=$RELEASE_ID" >> "$GITHUB_OUTPUT"

          if [ "$RELEASE_ID" == "null" ] || [ -z "$RELEASE_ID" ]; then
            echo "Failed to create release. Response content:"
            cat api_response.json
            exit 1
          fi

      - name: 📤 Upload Release Asset
        if: steps.check_commits.outputs.commit_count != '0'
        run: |
          RELEASE_ID="${{ steps.create_release.outputs.RELEASE_ID }}"
          ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
          FILE_PATH="./$ZIP_NAME"

          curl --fail -s -X POST "${{ gitea.api_url }}/repos/${{ gitea.repository }}/releases/$RELEASE_ID/assets?name=$ZIP_NAME" \
            -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
            -H "Content-Type: application/zip" \
            --data-binary @"$FILE_PATH"

      # ----- Docker steps -----
      - name: Clone Upstream Code (if needed)
        if: steps.check_commits.outputs.commit_count != '0' && (steps.check_upstream.outputs.upstream_needs_update == 'true' || steps.check_upstream.outputs.repo_url != '')
        run: |
          rm -rf upstream_src
          git clone --depth 1 --branch ${{ steps.check_upstream.outputs.repo_branch }} ${{ steps.check_upstream.outputs.repo_url }} upstream_src

      - name: 🔍 Check if Dockerfile exists
        if: steps.check_commits.outputs.commit_count != '0' || steps.check_upstream.outputs.upstream_needs_update == 'true'
        id: dockerfile_check
        run: |
          if [ -n "${{ steps.check_upstream.outputs.repo_url }}" ]; then
             if [ -f upstream_src/Dockerfile ]; then
                echo "exists=true" >> "$GITHUB_OUTPUT"
             else
                # Fallback or error? User said "ignore", but we need a dockerfile to build. 
                # Assuming if upstream_src is present, we trust it, or fail at build time.
                # Let's say exists=true and let build fail if missing, per user hint.
                echo "exists=true" >> "$GITHUB_OUTPUT"
             fi
          elif [ -f Dockerfile ]; then
            echo "exists=true" >> "$GITHUB_OUTPUT"
          else
            echo "exists=false" >> "$GITHUB_OUTPUT"
          fi

      - name: 🔐 Login to Gitea Container Registry
        if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
        uses: docker/login-action@v3
        with:
          registry: git.icc.gg
          username: ${{ gitea.actor }}
          password: ${{ secrets.CR_PAT }}

      - name: 🛠 Set up QEMU
        if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
        uses: docker/setup-qemu-action@v3

      - name: 🛠 Set up Docker Buildx
        if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
        uses: docker/setup-buildx-action@v3

      - name: 🐳 Build and Push Docker image
        if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
        uses: docker/build-push-action@v5
        id: docker_build
        with:
          context: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src' || '.' }}
          platforms: linux/amd64,linux/arm64
          file: ${{ steps.check_upstream.outputs.repo_url != '' && './upstream_src/Dockerfile' || './Dockerfile' }}
          push: true
          build-args: |
            ${{ steps.check_upstream.outputs.build_args }}
          tags: |
            git.icc.gg/${{ gitea.repository }}:latest
            git.icc.gg/${{ gitea.repository }}:${{ steps.version.outputs.VERSION }}
