name: Release Baron SSO to Staging

on:
  workflow_dispatch:
    inputs:
      rc_version_tag:
        description: "The version tag to deploy to staging (e.g., v1.2601.1-RC1)"
        required: true
        type: string

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup SSH
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.STAGE_SSH_PRIVATE_KEY }}

      - name: Deploy to Staging
        env:
          IMAGE_TAG: ${{ github.event.inputs.rc_version_tag }}
          BACKEND_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/backend
          USERFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/userfront
          ADMINFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/adminfront
          DEVFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/devfront
          ORGFRONT_IMAGE_NAME: ${{ vars.HARBOR_HOSTNAME }}/baron_sso/orgfront
          
          # Staging-specific variables
          DEPLOY_PATH: ${{ vars.STAGE_DEPLOY_PATH }}
          STAGE_HOST: ${{ vars.STAGE_HOST }}
          STAGE_USER: ${{ vars.STAGE_USER }}

          HARBOR_ENDPOINT: ${{ vars.HARBOR_ENDPOINT }}
          HARBOR_ROBOT_ACCOUNT: ${{ vars.HARBOR_ROBOT_ACCOUNT }}
          HARBOR_ROBOT_KEY: ${{ secrets.HARBOR_ROBOT_KEY }}
        run: |
          set -euo pipefail

          echo "DEBUG: STAGE_USER='${STAGE_USER}'"
          echo "DEBUG: STAGE_HOST='${STAGE_HOST}'"
          echo "DEBUG: DEPLOY_PATH='${DEPLOY_PATH}'"

          # Sanity check
          if [ -z "${STAGE_USER}" ] || [ -z "${STAGE_HOST}" ] || [ -z "${DEPLOY_PATH}" ]; then
            echo "::error::Missing required vars (STAGE_USER/STAGE_HOST/DEPLOY_PATH)."
            exit 1
          fi

          ssh-keyscan -H "${STAGE_HOST}" >> ~/.ssh/known_hosts
          ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p '${DEPLOY_PATH}'"

          # .env 파일 생성
          cat <<'EOF' > .env
          APP_ENV=stage
          BACKEND_LOG_LEVEL=debug
          CLIENT_LOG_DEBUG=true
          WORKS_ADMIN_API_BASE_URL=${{ vars.WORKS_ADMIN_API_BASE_URL }}
          WORKS_ADMIN_OAUTH_TOKEN_URL=${{ vars.WORKS_ADMIN_OAUTH_TOKEN_URL }}
          TZ=Asia/Seoul
          IDP_PROVIDER=ory

          # DB & Clickhouse
          DB_PORT=${{ vars.DB_PORT }}
          CLICKHOUSE_PORT_HTTP=${{ vars.CLICKHOUSE_PORT_HTTP }}
          CLICKHOUSE_PORT_NATIVE=${{ vars.CLICKHOUSE_PORT_NATIVE }}
          CLICKHOUSE_HOST=${{ vars.CLICKHOUSE_HOST }}
          CLICKHOUSE_USER=${{ vars.CLICKHOUSE_USER }}
          CLICKHOUSE_PASSWORD=${{ secrets.CLICKHOUSE_PASSWORD }}


          BACKEND_PORT=${{ vars.BACKEND_PORT }}
          ADMINFRONT_PORT=${{ vars.ADMINFRONT_PORT }}
          DEVFRONT_PORT=${{ vars.DEVFRONT_PORT }}
          ORGFRONT_PORT=${{ vars.ORGFRONT_PORT }}
          USERFRONT_PORT=${{ vars.USERFRONT_PORT }}
          
          OATHKEEPER_API_URL=${{ vars.OATHKEEPER_API_URL }}

          DB_USER=${{ vars.DB_USER }}
          DB_PASSWORD=${{ secrets.STG_DB_PASSWORD }}
          DB_NAME=${{ vars.DB_NAME }}
          COOKIE_SECRET=${{ secrets.STG_COOKIE_SECRET }}
          JWT_SECRET=${{ secrets.STG_JWT_SECRET }}
          REDIS_ADDR=${{ vars.REDIS_ADDR }}
          CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS }}
          AUDIT_WORKER_COUNT=5
          AUDIT_QUEUE_SIZE=2000
          PROFILE_CACHE_TTL=${{ vars.PROFILE_CACHE_TTL }}
          ORGFRONT_ORGCHART_CACHE_TTL_SECONDS=${{ vars.ORGFRONT_ORGCHART_CACHE_TTL_SECONDS }}
          NAVER_CLOUD_ACCESS_KEY=${{ vars.NAVER_CLOUD_ACCESS_KEY }}
          NAVER_CLOUD_SECRET_KEY=${{ secrets.NAVER_CLOUD_SECRET_KEY }}
          NAVER_CLOUD_SERVICE_ID=${{ vars.NAVER_CLOUD_SERVICE_ID }}
          NAVER_SENDER_PHONE_NUMBER=${{ vars.NAVER_SENDER_PHONE_NUMBER }}
          AWS_REGION=${{ vars.AWS_REGION }}
          AWS_ACCESS_KEY_ID=${{ vars.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}
          AWS_SES_SENDER=${{ vars.AWS_SES_SENDER }}
          ADMIN_EMAIL=${{ vars.ADMIN_EMAIL }}
          ADMIN_PASSWORD=${{ secrets.STG_ADMIN_PASSWORD }}
          USERFRONT_URL=${{ vars.USERFRONT_URL }}
          ORGFRONT_URL=${{ vars.ORGFRONT_URL }}
          BACKEND_PUBLIC_URL=${{ vars.BACKEND_URL }}
          BACKEND_URL=${{ vars.BACKEND_URL }}
          OATHKEEPER_PUBLIC_URL=${{ vars.OATHKEEPER_PUBLIC_URL }}
          ORY_POSTGRES_TAG=${{ vars.ORY_POSTGRES_TAG }}
          ORY_POSTGRES_USER=${{ vars.ORY_POSTGRES_USER }}
          ORY_POSTGRES_PASSWORD=${{ secrets.STG_ORY_POSTGRES_PASSWORD }}
          ORY_POSTGRES_DB=${{ vars.ORY_POSTGRES_DB }}
          KRATOS_DB=${{ vars.KRATOS_DB }}
          HYDRA_DB=${{ vars.HYDRA_DB }}
          KETO_DB=${{ vars.KETO_DB }}
          KRATOS_VERSION=${{ vars.KRATOS_VERSION }}
          KRATOS_UI_NODE_VERSION=${{ vars.KRATOS_UI_NODE_VERSION }}
          HYDRA_VERSION=${{ vars.HYDRA_VERSION }}
          KETO_VERSION=${{ vars.KETO_VERSION }}
          ORY_SDK_URL=${{ vars.ORY_SDK_URL }}
          KRATOS_PUBLIC_URL=${{ vars.KRATOS_PUBLIC_URL }}
          KRATOS_ADMIN_URL=${{ vars.KRATOS_ADMIN_URL }}
          KRATOS_BROWSER_URL=${{ vars.KRATOS_BROWSER_URL }}
          KRATOS_UI_URL=${{ vars.KRATOS_UI_URL }}
          HYDRA_ADMIN_URL=${{ vars.HYDRA_ADMIN_URL }}
          HYDRA_PUBLIC_URL=${{ vars.HYDRA_PUBLIC_URL }}
          JWKS_URL=${{ vars.JWKS_URL }}
          OATHKEEPER_VERSION=${{ vars.OATHKEEPER_VERSION }}
          OATHKEEPER_UID=${{ vars.OATHKEEPER_UID }}
          OATHKEEPER_GID=${{ vars.OATHKEEPER_GID }}
          OATHKEEPER_HEALTH_URL=${{ vars.OATHKEEPER_HEALTH_URL }}
          OATHKEEPER_HEALTH_INTERVAL_SECONDS=${{ vars.OATHKEEPER_HEALTH_INTERVAL_SECONDS }}
          OATHKEEPER_HEALTH_TIMEOUT_SECONDS=${{ vars.OATHKEEPER_HEALTH_TIMEOUT_SECONDS }}
          OATHKEEPER_HEALTH_ENABLED=${{ vars.OATHKEEPER_HEALTH_ENABLED }}
          CSRF_COOKIE_NAME=${{ vars.CSRF_COOKIE_NAME }}
          CSRF_COOKIE_SECRET=${{ secrets.STG_CSRF_COOKIE_SECRET }}
          
          VITE_OIDC_AUTHORITY=${{ vars.VITE_OIDC_AUTHORITY }}
          ADMINFRONT_CALLBACK_URLS=${{ vars.ADMINFRONT_CALLBACK_URLS }}
          DEVFRONT_CALLBACK_URLS=${{ vars.DEVFRONT_CALLBACK_URLS }}
          ORGFRONT_CALLBACK_URLS=${{ vars.ORGFRONT_CALLBACK_URLS }}
          # OATHKEEPER_INTROSPECT_CLIENT_ID=${{ vars.OATHKEEPER_INTROSPECT_CLIENT_ID }}
          # OATHKEEPER_INTROSPECT_CLIENT_SECRET=${{ secrets.STG_OATHKEEPER_INTROSPECT_CLIENT_SECRET }}
          EOF

          if ! grep -Eq "^ORGFRONT_ORGCHART_CACHE_TTL_SECONDS=.+" .env; then
            sed -i "s/^ORGFRONT_ORGCHART_CACHE_TTL_SECONDS=.*/ORGFRONT_ORGCHART_CACHE_TTL_SECONDS=3600/" .env
          fi

          required_dotenv_keys="
          APP_ENV BACKEND_LOG_LEVEL CLIENT_LOG_DEBUG WORKS_ADMIN_API_BASE_URL WORKS_ADMIN_OAUTH_TOKEN_URL TZ IDP_PROVIDER
          DB_PORT CLICKHOUSE_PORT_HTTP CLICKHOUSE_PORT_NATIVE CLICKHOUSE_HOST CLICKHOUSE_USER CLICKHOUSE_PASSWORD
          BACKEND_PORT ADMINFRONT_PORT DEVFRONT_PORT ORGFRONT_PORT USERFRONT_PORT OATHKEEPER_API_URL
          DB_USER DB_PASSWORD DB_NAME COOKIE_SECRET JWT_SECRET REDIS_ADDR CORS_ALLOWED_ORIGINS PROFILE_CACHE_TTL
          ORGFRONT_ORGCHART_CACHE_TTL_SECONDS
          NAVER_CLOUD_ACCESS_KEY NAVER_CLOUD_SECRET_KEY NAVER_CLOUD_SERVICE_ID NAVER_SENDER_PHONE_NUMBER
          AWS_REGION AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SES_SENDER ADMIN_EMAIL ADMIN_PASSWORD
          USERFRONT_URL ORGFRONT_URL BACKEND_PUBLIC_URL BACKEND_URL OATHKEEPER_PUBLIC_URL
          ORY_POSTGRES_TAG ORY_POSTGRES_USER ORY_POSTGRES_PASSWORD ORY_POSTGRES_DB KRATOS_DB HYDRA_DB KETO_DB
          KRATOS_VERSION KRATOS_UI_NODE_VERSION HYDRA_VERSION KETO_VERSION ORY_SDK_URL KRATOS_PUBLIC_URL
          KRATOS_ADMIN_URL KRATOS_BROWSER_URL KRATOS_UI_URL HYDRA_ADMIN_URL HYDRA_PUBLIC_URL JWKS_URL
          OATHKEEPER_VERSION OATHKEEPER_UID OATHKEEPER_GID OATHKEEPER_HEALTH_URL OATHKEEPER_HEALTH_INTERVAL_SECONDS
          OATHKEEPER_HEALTH_TIMEOUT_SECONDS OATHKEEPER_HEALTH_ENABLED CSRF_COOKIE_NAME CSRF_COOKIE_SECRET
          VITE_OIDC_AUTHORITY ADMINFRONT_CALLBACK_URLS DEVFRONT_CALLBACK_URLS ORGFRONT_CALLBACK_URLS
          "
          for key in ${required_dotenv_keys}; do
            if ! grep -Eq "^${key}=.+" .env; then
              echo "::error::Missing required staging .env value: ${key}. Check Gitea repo variables/secrets."
              exit 1
            fi
          done

          # 파일 복사
          ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p ${DEPLOY_PATH}/docker"
          ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p ${DEPLOY_PATH}/adminfront"
          ssh "${STAGE_USER}@${STAGE_HOST}" "mkdir -p ${DEPLOY_PATH}/scripts"
          
          # [중요] docker/ory 폴더 복사 (여기에 init-db/1-createdb.sql이 있어야 함)
          scp -r docker/ory "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/docker/"
          
          if [ -d "docker/init-metadata" ]; then
            scp -r docker/init-metadata "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/docker/"
          fi
          
          if [ -d "gateway" ]; then
             scp -r gateway "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/"
          fi

          scp adminfront/seed-tenant.csv "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/adminfront/"
          scp scripts/render_ory_config.sh "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/scripts/"
          scp docker/docker-compose.staging.template.yaml .env "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/"
          scp docker/compose.infra.yaml "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/compose.infra.yml"
          scp compose.ory.yaml "${STAGE_USER}@${STAGE_HOST}:${DEPLOY_PATH}/compose.ory.yml"

          # 배포 실행
          echo "${HARBOR_ROBOT_KEY}" | ssh "${STAGE_USER}@${STAGE_HOST}" \
            "export DEPLOY_PATH='${DEPLOY_PATH}'; \
             export BACKEND_IMAGE_NAME='${BACKEND_IMAGE_NAME}'; \
             export USERFRONT_IMAGE_NAME='${USERFRONT_IMAGE_NAME}'; \
             export ADMINFRONT_IMAGE_NAME='${ADMINFRONT_IMAGE_NAME}'; \
             export DEVFRONT_IMAGE_NAME='${DEVFRONT_IMAGE_NAME}'; \
             export ORGFRONT_IMAGE_NAME='${ORGFRONT_IMAGE_NAME}'; \
             export IMAGE_TAG='${IMAGE_TAG}'; \
             export HARBOR_ENDPOINT='${HARBOR_ENDPOINT}'; \
             export HARBOR_ROBOT_ACCOUNT='${HARBOR_ROBOT_ACCOUNT}'; \
             cd \"\${DEPLOY_PATH}\"; \
             docker login \"\${HARBOR_ENDPOINT}\" -u \"\${HARBOR_ROBOT_ACCOUNT}\" --password-stdin; \
             set -a; . ./.env; set +a; \
             
             # 네트워크 생성
             for net in baron_net public_net ory-net hydranet kratosnet; do
               docker network inspect \"\$net\" >/dev/null 2>&1 || docker network create \"\$net\"
             done

             bash scripts/render_ory_config.sh; \
             chmod -R 777 config/.generated/ory || true; \
             
             envsubst < docker-compose.staging.template.yaml > docker-compose.yml; \

             # [중요] 설정 파일 권한 문제 해결 (Ory 이미지는 root가 아닌 사용자로 실행됨)
             chmod -R 777 docker/ory
             
             docker compose -f compose.infra.yml -f compose.ory.yml -f docker-compose.yml pull; \
             
             # [주의] DB 초기화 스크립트는 '새로운 볼륨'에서만 실행됨. 
             # DB 초기화 문제를 확실히 해결하기 위해 기존 볼륨을 날리고 다시 띄움 (데이터 삭제됨 주의)
             # 스테이징이므로 초기화 진행. 데이터 보존이 필요하면 이 줄 제거하고 수동으로 DB 만들어야 함.
             docker compose -f compose.infra.yml -f compose.ory.yml -f docker-compose.yml down -v || true
             
             docker compose -f compose.infra.yml -f compose.ory.yml -f docker-compose.yml up -d --remove-orphans; \
             
             # 배포 후 상태 확인 (실패 시 로그 출력을 위함)
             sleep 10; \
             if [ \$(docker inspect -f '{{.State.ExitCode}}' baron-sso-staging-kratos-migrate-1) -ne 0 ]; then \
                echo 'Kratos Migrate Failed. Logs:'; \
                docker logs baron-sso-staging-kratos-migrate-1; \
                exit 1; \
             fi"
