# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Build MokoGitea Docker image, push to registry, and deploy

name: Deploy MokoGitea

on:
  push:
    branches:
      - main
  workflow_dispatch:
    inputs:
      version:
        description: 'Version tag (e.g. v1.26.1+MOKO06.12.00)'
        required: true
        default: 'latest'
      environment:
        description: 'Target environment'
        required: true
        default: 'dev'
        type: choice
        options:
          - dev
          - production

concurrency:
  group: deploy-mokogitea
  cancel-in-progress: false

env:
  REGISTRY: code.mokoconsulting.tech
  IMAGE: mokoconsulting/mokogitea
  DEPLOY_HOST: code.mokoconsulting.tech
  DEPLOY_PORT: 2918
  DEPLOY_USER: mokoconsulting
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source (for version detection)
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Determine settings
        id: config
        run: |
          # On push to main, auto-deploy to production with git-derived version.
          # On workflow_dispatch, use the provided inputs.
          if [ "${{ github.event_name }}" = "push" ]; then
            VERSION=$(git describe --tags --always 2>/dev/null || echo "dev-$(git rev-parse --short HEAD)")
            ENV="production"
          else
            VERSION="${{ github.event.inputs.version }}"
            ENV="${{ github.event.inputs.environment }}"
          fi

          if [ "$ENV" = "production" ]; then
            echo "compose_dir=/opt/gitea" >> $GITHUB_OUTPUT
            echo "container=mokogitea" >> $GITHUB_OUTPUT
            echo "source_dir=/opt/gitea/source" >> $GITHUB_OUTPUT
            echo "branch=main" >> $GITHUB_OUTPUT
            echo "tag=${VERSION}" >> $GITHUB_OUTPUT
            echo "instance_url=https://code.mokoconsulting.tech" >> $GITHUB_OUTPUT
          else
            echo "compose_dir=/opt/gitea-dev" >> $GITHUB_OUTPUT
            echo "container=mokogitea-dev" >> $GITHUB_OUTPUT
            echo "source_dir=/opt/gitea-dev/source" >> $GITHUB_OUTPUT
            echo "branch=dev" >> $GITHUB_OUTPUT
            echo "tag=${VERSION}-dev" >> $GITHUB_OUTPUT
            echo "instance_url=https://git.dev.mokoconsulting.tech" >> $GITHUB_OUTPUT
          fi

      - name: Enable maintenance mode
        env:
          GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
          INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
        run: |
          echo "Enabling maintenance mode on ${INSTANCE_URL}..."
          curl -sf -X POST \
            -H "Authorization: token ${GITEA_TOKEN}" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            "${INSTANCE_URL}/-/admin/config" \
            -d 'key=instance.maintenance_mode&value={"AdminWebAccessOnly":true}' \
            || echo "WARNING: Could not enable maintenance mode (instance may be down)"

      - name: Build and deploy via SSH
        env:
          SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
          TAG: ${{ steps.config.outputs.tag }}
          BRANCH: ${{ steps.config.outputs.branch }}
          SOURCE_DIR: ${{ steps.config.outputs.source_dir }}
          COMPOSE_DIR: ${{ steps.config.outputs.compose_dir }}
          CONTAINER: ${{ steps.config.outputs.container }}
        run: |
          mkdir -p ~/.ssh
          echo "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
          chmod 600 ~/.ssh/deploy_key

          SSH_CMD="ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} -o ConnectTimeout=30 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}"

          $SSH_CMD "echo 'SSH connected'"

          # Pre-deploy cleanup: free disk and memory for the build
          $SSH_CMD "
            echo 'Cleaning Docker build cache and unused images...'
            docker builder prune -af 2>/dev/null || true
            docker image prune -af 2>/dev/null || true
            echo 'Clearing swap...'
            sudo swapoff -a && sudo swapon -a 2>/dev/null || true
            echo 'Cleanup complete'
            free -m | head -3
          "

          # Pull latest source
          $SSH_CMD "
            set -e
            if [ ! -d ${SOURCE_DIR}/.git ]; then
              git clone -b ${BRANCH} https://code.mokoconsulting.tech/MokoConsulting/MokoGitea.git ${SOURCE_DIR}
            fi
            cd ${SOURCE_DIR}
            git fetch origin ${BRANCH}
            git reset --hard origin/${BRANCH}
          "

          # Build Docker image
          $SSH_CMD "
            set -e
            cd ${SOURCE_DIR}
            docker build --no-cache --build-arg GOFLAGS='-p 1' \
              --tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:${TAG} \
              --tag ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest \
              -f Dockerfile .
          "

          # Push to container registry
          $SSH_CMD "
            set -e
            docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:${TAG}
            docker push ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest
          "

          # Update compose and restart
          $SSH_CMD "
            set -e
            cd ${COMPOSE_DIR}
            sed -i 's|${{ env.IMAGE }}:[^ ]*|${{ env.IMAGE }}:${TAG}|' docker-compose.yml
            docker compose up -d ${CONTAINER}
          "

          # Health check
          $SSH_CMD "
            for i in 1 2 3 4 5 6 7 8; do
              sleep 15
              if docker inspect --format='{{.State.Health.Status}}' ${CONTAINER} 2>/dev/null | grep -q healthy; then
                echo 'Container healthy!'
                docker inspect --format='Image: {{.Config.Image}}' ${CONTAINER}
                exit 0
              fi
              echo \"Waiting... (attempt \$i/8)\"
            done
            echo 'Health check failed'
            docker logs ${CONTAINER} --tail 20
            exit 1
          "

      - name: Update updates.xml
        if: success()
        env:
          GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
          TAG: ${{ steps.config.outputs.tag }}
          INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
          DEPLOY_ENV: ${{ github.event.inputs.environment || 'production' }}
        run: |
          # Only update updates.xml for production stable releases
          if [ "$DEPLOY_ENV" != "production" ]; then
            echo "Skipping updates.xml — dev deployments don't update stable channel"
            exit 0
          fi

          # Extract project version by stripping the version prefix from the tag.
          # Reads prefix from manifest API (e.g. "v1.26.1+MOKO"), falls back to legacy pattern.
          API_BASE="https://${REGISTRY}/api/v1/repos/MokoConsulting/MokoGitea"
          PREFIX=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
            "${API_BASE}/manifest" | python3 -c "import json,sys; print(json.load(sys.stdin).get('version_prefix',''))" 2>/dev/null || true)

          if [ -n "$PREFIX" ]; then
            MOKO_VER="${TAG#$PREFIX}"
          else
            # Legacy fallback: strip everything up to and including "-moko."
            MOKO_VER=$(echo "$TAG" | sed -n 's/.*-moko\.\(.*\)/\1/p')
          fi

          if [ -z "$MOKO_VER" ]; then
            echo "Could not extract version from tag: $TAG (prefix: ${PREFIX:-none})"
            exit 0
          fi

          RELEASE_URL="https://${REGISTRY}/MokoConsulting/MokoGitea/releases/tag/${TAG}"
          DOCKER_IMG="${REGISTRY}/${IMAGE}:${TAG}"

          python3 << PYEOF
          import json, os, re, base64, urllib.request

          token = os.environ["GITEA_TOKEN"]
          registry = os.environ["REGISTRY"]
          tag = os.environ["TAG"]
          moko_ver = os.environ["MOKO_VER"]
          release_url = os.environ["RELEASE_URL"]
          docker_img = os.environ["DOCKER_IMG"]
          api = f"https://{registry}/api/v1/repos/MokoConsulting/MokoGitea"

          # Fetch current updates.xml
          req = urllib.request.Request(f"{api}/contents/updates.xml?ref=main",
              headers={"Authorization": f"token {token}"})
          with urllib.request.urlopen(req) as resp:
              data = json.loads(resp.read())
          sha = data["sha"]
          content = base64.b64decode(data["content"]).decode("utf-8")

          # Update stable channel — match the <update> block containing <tag>stable</tag>
          def replace_channel(xml, channel, ver, url, docker):
              pattern = rf"(<update>\s*<name>MokoGitea</name>[\s\S]*?<tags><tag>{channel}</tag></tags>[\s\S]*?</update>)"
              def replacer(m):
                  block = m.group(1)
                  block = re.sub(r"<version>[^<]*</version>", f"<version>{ver}</version>", block)
                  block = re.sub(r"(<infourl[^>]*>)[^<]*(</infourl>)", rf"\1{url}\2", block)
                  block = re.sub(r"(<downloadurl[^>]*>)[^<]*(</downloadurl>)", rf"\1{docker}\2", block)
                  return block
              return re.sub(pattern, replacer, xml)

          content = replace_channel(content, "stable", moko_ver, release_url, docker_img)
          content = re.sub(r"VERSION: [^\n]*", f"VERSION: {moko_ver}", content)

          # Push updated file
          encoded = base64.b64encode(content.encode()).decode()
          payload = json.dumps({
              "message": f"chore(ci): update updates.xml to {moko_ver}",
              "content": encoded,
              "sha": sha,
              "branch": "main",
          }).encode()
          req = urllib.request.Request(f"{api}/contents/updates.xml",
              data=payload, method="PUT",
              headers={"Authorization": f"token {token}", "Content-Type": "application/json"})
          with urllib.request.urlopen(req) as resp:
              print(f"updates.xml updated to {moko_ver}")
          PYEOF

      - name: Disable maintenance mode
        if: always()
        env:
          GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
          INSTANCE_URL: ${{ steps.config.outputs.instance_url }}
        run: |
          echo "Disabling maintenance mode on ${INSTANCE_URL}..."
          curl -sf -X POST \
            -H "Authorization: token ${GITEA_TOKEN}" \
            -H "Content-Type: application/x-www-form-urlencoded" \
            "${INSTANCE_URL}/-/admin/config" \
            -d 'key=instance.maintenance_mode&value={"AdminWebAccessOnly":false}' \
            || echo "WARNING: Could not disable maintenance mode"

      - name: Verify
        run: |
          sleep 5
          curl -sf https://${{ env.DEPLOY_HOST }}/api/healthz && echo " — API healthy"

      - name: Notify on failure
        if: failure()
        run: echo "::error::Deploy failed for ${{ steps.config.outputs.tag }}"
