# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later
# BRIEF: Sync upstream Gitea bug fixes into MokoGitea issue tracker

name: Upstream Bug Sync

on:
  schedule:
    - cron: '0 8 * * *'  # daily at 08:00 UTC
  workflow_dispatch:
    inputs:
      days_back:
        description: 'How many days back to scan (default: 7)'
        required: false
        default: '7'

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - name: Sync upstream bugs
        env:
          GH_TOKEN: ${{ secrets.GH_MIRROR_TOKEN }}
          MOKOGITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
          MOKOGITEA_URL: https://code.mokoconsulting.tech
          MOKOGITEA_REPO: MokoConsulting/MokoGitea
          UPSTREAM_BRANCH: release/v1.26
          DAYS_BACK: ${{ github.event.inputs.days_back || '7' }}
        run: |
          python3 << 'PYEOF'
          import json, os, re, sys, urllib.parse, urllib.request
          from datetime import datetime, timedelta, timezone

          GH_TOKEN = os.environ["GH_TOKEN"]
          MOKO_TOKEN = os.environ["MOKOGITEA_TOKEN"]
          MOKO_URL = os.environ["MOKOGITEA_URL"]
          MOKO_REPO = os.environ["MOKOGITEA_REPO"]
          BRANCH = os.environ["UPSTREAM_BRANCH"]
          DAYS = int(os.environ.get("DAYS_BACK", "7"))

          # Label IDs in MokoGitea
          LABELS = {
              "type_bug": 5757, "upstream": 5758, "security": 5032,
              "critical": 5018, "high": 5019, "medium": 5020, "low": 5021,
          }

          def gh_get(url):
              req = urllib.request.Request(url, headers={
                  "Authorization": f"token {GH_TOKEN}",
                  "Accept": "application/vnd.github.v3+json",
              })
              with urllib.request.urlopen(req) as r:
                  return json.loads(r.read())

          def moko_get(path):
              req = urllib.request.Request(f"{MOKO_URL}/api/v1/{path}", headers={
                  "Authorization": f"token {MOKO_TOKEN}",
              })
              with urllib.request.urlopen(req) as r:
                  return json.loads(r.read())

          def moko_post(path, data):
              payload = json.dumps(data).encode()
              req = urllib.request.Request(f"{MOKO_URL}/api/v1/{path}",
                  data=payload, method="POST", headers={
                      "Authorization": f"token {MOKO_TOKEN}",
                      "Content-Type": "application/json",
                  })
              with urllib.request.urlopen(req) as r:
                  return json.loads(r.read())

          # ── Step 1: Find recently merged upstream PRs ──
          since = (datetime.now(timezone.utc) - timedelta(days=DAYS)).strftime("%Y-%m-%dT%H:%M:%SZ")
          query = f"repo:go-gitea/gitea is:pr is:merged base:{BRANCH} merged:>={since}"
          encoded = urllib.parse.quote(query)
          print(f"Scanning: {query}")

          result = gh_get(f"https://api.github.com/search/issues?q={encoded}&per_page=100&sort=updated&order=desc")
          total = result["total_count"]
          print(f"Found {total} merged PRs in the last {DAYS} days")

          if total == 0:
              print("Nothing to sync.")
              sys.exit(0)

          # ── Step 2: Filter for bug/security fixes ──
          bugs = []
          for pr in result["items"]:
              title = pr["title"]
              label_names = [l["name"].lower() for l in pr.get("labels", [])]
              is_fix = title.lower().startswith("fix")
              is_security = any("security" in l for l in label_names) or "[security]" in title.lower()
              is_bug = any("bug" in l for l in label_names)

              if not (is_fix or is_security or is_bug):
                  continue

              refs = re.findall(r"#(\d+)", title)
              severity = "critical" if is_security and "[security]" in title.lower() else \
                         "high" if is_security else "medium"

              bugs.append({
                  "number": pr["number"], "title": title, "url": pr["html_url"],
                  "severity": severity, "is_security": is_security, "refs": refs,
                  "merged": pr.get("pull_request", {}).get("merged_at", "")[:10],
              })

          print(f"Filtered to {len(bugs)} bug/security fixes")
          if not bugs:
              sys.exit(0)

          # ── Step 3: Collect already-tracked PR numbers ──
          tracked = set()
          for state in ["open", "closed"]:
              try:
                  issues = moko_get(f"repos/{MOKO_REPO}/issues?state={state}&type=issues&limit=50&labels=upstream")
                  for iss in issues:
                      text = (iss.get("body") or "") + " " + (iss.get("title") or "")
                      tracked.update(re.findall(r"(?:#|/pull/)(\d{4,})", text))
              except Exception:
                  pass

          print(f"Already tracked: {len(tracked)} upstream PRs")

          # ── Step 4: Create issues for new bugs ──
          created = skipped = errors = 0
          for bug in bugs:
              if any(r in tracked for r in bug["refs"]):
                  print(f"  SKIP #{bug['number']}: {bug['title'][:55]} (tracked)")
                  skipped += 1
                  continue

              labels = [LABELS["type_bug"], LABELS["upstream"], LABELS[bug["severity"]]]
              if bug["is_security"]:
                  labels.append(LABELS["security"])

              body = (
                  f"## Summary\n\n"
                  f"Upstream bug fix merged into `{BRANCH}`.\n\n"
                  f"## Upstream Reference\n\n"
                  f"- PR: {bug['url']}\n"
                  f"- Merged: {bug['merged']}\n"
                  f"- Branch: {BRANCH}\n\n"
                  f"## Severity: {bug['severity'].title()}"
                  f"{'  (security)' if bug['is_security'] else ''}\n\n"
                  f"## Action\n\n"
                  f"Cherry-pick from upstream `{BRANCH}` branch.\n\n"
                  f"---\n"
                  f"*Auto-created by upstream-bug-sync workflow*\n"
                  f"*Authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>*"
              )

              try:
                  iss = moko_post(f"repos/{MOKO_REPO}/issues", {
                      "title": bug["title"], "body": body, "labels": labels,
                  })
                  print(f"  CREATED #{iss['number']}: {bug['title'][:55]}")
                  created += 1
              except Exception as e:
                  print(f"  ERROR #{bug['number']}: {e}")
                  errors += 1

          print(f"\n=== Done: {created} created, {skipped} skipped, {errors} errors ===")
          PYEOF
