name: Update script timestamp on .sh changes

on:
  push:
    branches:
      - main
    paths:
      - "ct/**/*.sh"
      - "install/**/*.sh"
      - "tools/**/*.sh"
      - "turnkey/**/*.sh"
      - "vm/**/*.sh"

jobs:
  update-script-timestamp:
    runs-on: self-hosted
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get changed .sh files and derive slugs
        id: slugs
        run: |
          changed=$(git diff --name-only "${{ github.event.before }}" "${{ github.event.after }}" -- ct/ install/ tools/ turnkey/ vm/ | grep '\.sh$' || true)
          if [[ -z "$changed" ]]; then
            echo "No .sh files changed in ct/, install/, tools/, turnkey/, or vm/."
            echo "count=0" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          declare -A seen
          slugs=""
          for f in $changed; do
            [[ -f "$f" ]] || continue
            base="${f##*/}"
            base="${base%.sh}"
            if [[ "$f" == install/* && "$base" == *-install ]]; then
              slug="${base%-install}"
            else
              slug="$base"
            fi
            if [[ -z "${seen[$slug]:-}" ]]; then
              seen[$slug]=1
              slugs="$slugs $slug"
            fi
          done
          slugs=$(echo $slugs | xargs -n1 | sort -u)
          if [[ -z "$slugs" ]]; then
            echo "No slugs to update."
            echo "count=0" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          echo "$slugs" > changed_slugs.txt
          echo "count=$(echo "$slugs" | wc -w)" >> "$GITHUB_OUTPUT"

      - name: Parse PR number from merge commit
        id: pr
        run: |
          re='#([0-9]+)'
          if [[ "$COMMIT_MSG" =~ $re ]]; then
            echo "number=${BASH_REMATCH[1]}" >> "$GITHUB_OUTPUT"
          else
            echo "number=" >> "$GITHUB_OUTPUT"
          fi
        env:
          COMMIT_MSG: ${{ github.event.head_commit.message }}

      - name: Update script timestamps in PocketBase
        if: steps.slugs.outputs.count != '0'
        env:
          POCKETBASE_URL: ${{ secrets.POCKETBASE_URL }}
          POCKETBASE_COLLECTION: ${{ secrets.POCKETBASE_COLLECTION }}
          POCKETBASE_ADMIN_EMAIL: ${{ secrets.POCKETBASE_ADMIN_EMAIL }}
          POCKETBASE_ADMIN_PASSWORD: ${{ secrets.POCKETBASE_ADMIN_PASSWORD }}
          COMMIT_URL: ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}
          PR_URL: ${{ steps.pr.outputs.number != '' && format('{0}/{1}/pull/{2}', github.server_url, github.repository, steps.pr.outputs.number) || '' }}
        run: |
          node << 'ENDSCRIPT'
          (async function() {
            const fs = require('fs');
            const https = require('https');
            const http = require('http');
            const url = require('url');

            function request(fullUrl, opts) {
              return new Promise(function(resolve, reject) {
                const u = url.parse(fullUrl);
                const isHttps = u.protocol === 'https:';
                const body = opts.body;
                const options = {
                  hostname: u.hostname,
                  port: u.port || (isHttps ? 443 : 80),
                  path: u.path,
                  method: opts.method || 'GET',
                  headers: opts.headers || {}
                };
                if (body) options.headers['Content-Length'] = Buffer.byteLength(body);
                const lib = isHttps ? https : http;
                const req = lib.request(options, function(res) {
                  let data = '';
                  res.on('data', function(chunk) { data += chunk; });
                  res.on('end', function() {
                    resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, statusCode: res.statusCode, body: data });
                  });
                });
                req.on('error', reject);
                if (body) req.write(body);
                req.end();
              });
            }

            const raw = process.env.POCKETBASE_URL.replace(/\/$/, '');
            const apiBase = /\/api$/i.test(raw) ? raw : raw + '/api';
            const coll = process.env.POCKETBASE_COLLECTION;
            const slugsText = fs.readFileSync('changed_slugs.txt', 'utf8').trim();
            const slugs = slugsText ? slugsText.split(/\s+/).filter(Boolean) : [];
            if (slugs.length === 0) {
              console.log('No slugs to update.');
              return;
            }

            const authUrl = apiBase + '/collections/users/auth-with-password';
            const authBody = JSON.stringify({
              identity: process.env.POCKETBASE_ADMIN_EMAIL,
              password: process.env.POCKETBASE_ADMIN_PASSWORD
            });
            const authRes = await request(authUrl, {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: authBody
            });
            if (!authRes.ok) {
              throw new Error('Auth failed: ' + authRes.body);
            }
            const token = JSON.parse(authRes.body).token;
            const recordsUrl = apiBase + '/collections/' + encodeURIComponent(coll) + '/records';

            for (const slug of slugs) {
              const filter = "(slug='" + slug.replace(/'/g, "''") + "')";
              const listRes = await request(recordsUrl + '?filter=' + encodeURIComponent(filter) + '&perPage=1', {
                headers: { 'Authorization': token }
              });
              const list = JSON.parse(listRes.body);
              const record = list.items && list.items[0];
              if (!record) {
                console.log('Slug not in DB, skipping: ' + slug);
                continue;
              }
              const patchRes = await request(recordsUrl + '/' + record.id, {
                method: 'PATCH',
                headers: { 'Authorization': token, 'Content-Type': 'application/json' },
                body: JSON.stringify({
                  name: record.name || record.slug,
                  last_update_commit: process.env.PR_URL || process.env.COMMIT_URL || ''
                })
              });
              if (!patchRes.ok) {
                console.warn('PATCH failed for slug ' + slug + ': ' + patchRes.body);
                continue;
              }
              console.log('Updated timestamp for slug: ' + slug);
            }
            console.log('Done.');
          })().catch(e => { console.error(e); process.exit(1); });
          ENDSCRIPT
        shell: bash
