name: Repo Saturation Optimizer API (R2)

on:
  workflow_dispatch:

jobs:
  saturation:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repo
        uses: actions/checkout@v4

      - name: Run USAD + FRAI via API
        env:
          SAT_API_URL: ${{ secrets.SAT_API_URL }}
          SAT_API_TOKEN: ${{ secrets.SAT_API_TOKEN }}
        run: |
          node <<'NODE'
          const fs = require('fs');
          const path = require('path');
          const https = require('https');
          const { URL } = require('url');

          const CODE_EXTS = [
            '.ts', '.tsx', '.js', '.jsx',
            '.py', '.go', '.rs',
            '.java', '.cs',
            '.cpp', '.c', '.hpp', '.h'
          ];

          const IGNORE_DIRS = new Set([
            'node_modules',
            '.git',
            'dist',
            'build',
            'out',
            '.next',
            '.cache',
            'vendor',
            '.venv',
            '.vscode',
            'coverage'
          ]);

          function log(msg) {
            console.log(`[repo-sat] ${msg}`);
          }

          async function walk(dir, root, acc) {
            const entries = await fs.promises.readdir(dir, { withFileTypes: true });
            for (const entry of entries) {
              const full = path.join(dir, entry.name);
              if (entry.isDirectory()) {
                if (!IGNORE_DIRS.has(entry.name)) {
                  await walk(full, root, acc);
                }
              } else if (entry.isFile()) {
                const rel = path.relative(root, full);
                const ext = path.extname(entry.name).toLowerCase();
                if (CODE_EXTS.includes(ext)) {
                  acc.push(rel);
                }
              }
            }
          }

          async function computeFeaturesForFile(relPath, root) {
            const full = path.join(root, relPath);
            const text = await fs.promises.readFile(full, 'utf8');
            const lines = text.split(/\r?\n/);
            const loc = lines.length;

            const complexityRegex = /\b(if|for|while|switch|case|catch|elif|foreach|when)\b/g;
            const importRegex = /\b(import|require\(|from\s+["'`]|#include\s*<|using\s+[\w.]+;)\b/g;
            const todoRegex = /\b(TODO|FIXME)\b/g;

            const complexityMatches = text.match(complexityRegex) || [];
            const importMatches = text.match(importRegex) || [];
            const todoMatches = text.match(todoRegex) || [];

            const stat = await fs.promises.stat(full);
            const sizeBytes = stat.size;
            const sizeLog = sizeBytes > 0 ? Math.log10(sizeBytes) : 0;

            const ext = path.extname(relPath).toLowerCase();
            const extIndex = Math.max(0, CODE_EXTS.indexOf(ext));
            const extBucket = extIndex >= 0 ? extIndex + 1 : 0;

            const features = [
              loc,
              complexityMatches.length,
              importMatches.length,
              sizeLog,
              todoMatches.length,
              extBucket
            ];

            return { id: relPath, features };
          }

          function postJson(urlStr, token, data) {
            return new Promise((resolve, reject) => {
              const url = new URL(urlStr);
              const body = Buffer.from(JSON.stringify(data));

              const options = {
                hostname: url.hostname,
                port: url.port || 443,
                path: url.pathname + url.search,
                method: 'POST',
                headers: {
                  'Content-Type': 'application/json',
                  'Content-Length': body.length,
                  'Authorization': `Bearer ${token}`
                }
              };

              const req = https.request(options, (res) => {
                let chunks = '';
                res.on('data', (d) => (chunks += d.toString('utf8')));
                res.on('end', () => {
                  if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
                    try {
                      const json = JSON.parse(chunks);
                      resolve(json);
                    } catch (err) {
                      reject(new Error('Failed to parse JSON: ' + err.message));
                    }
                  } else {
                    reject(new Error(`Engine error ${res.statusCode}: ${chunks}`));
                  }
                });
              });

              req.on('error', (err) => reject(err));
              req.write(body);
              req.end();
            });
          }

          (async () => {
            const root = process.cwd();
            const engineUrl = process.env.SAT_API_URL;
            const engineToken = process.env.SAT_API_TOKEN;

            if (!engineUrl || !engineToken) {
              throw new Error('SAT_API_URL and SAT_API_TOKEN must be set as secrets.');
            }

            log(`Root: ${root}`);

            const relFiles = [];
            await walk(root, root, relFiles);
            log(`Code files detected: ${relFiles.length}`);

            const featureFiles = [];
            for (const rel of relFiles) {
              featureFiles.push(await computeFeaturesForFile(rel, root));
            }

            const payload = {
              repo: process.env.GITHUB_REPOSITORY || null,
              commit: process.env.GITHUB_SHA || null,
              files: featureFiles
            };

            log('Calling saturation API...');
            const result = await postJson(engineUrl, engineToken, payload);

            const outPath = path.join(root, 'repo-sat-report.json');
            await fs.promises.writeFile(outPath, JSON.stringify(result, null, 2), 'utf8');

            log(
              `Report written to repo-sat-report.json • FRAI=${(result.frai * 100).toFixed(
                2
              )}% • files=${result.file_count} • anomalies=${result.anomalies}`
            );
          })().catch((err) => {
            console.error('[repo-sat] ERROR:', err.message || err);
            process.exit(1);
          });
          NODE

      - name: Upload USAD+USL Report
        uses: actions/upload-artifact@v4
        with:
          name: repo-sat-report
          path: repo-sat-report.json
