name: AI Triage

on:
  issues:
    types: [opened]
  pull_request_target:
    types: [opened]

permissions:
  issues: write
  pull-requests: write

jobs:
  triage:
    runs-on: blacksmith-4vcpu-ubuntu-2404
    steps:
      - uses: actions/checkout@v6
        with:
          sparse-checkout: |
            VISION.md
            CONTRIBUTING.md
          sparse-checkout-cone-mode: false

      - name: Triage with Claude
        uses: actions/github-script@v7
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        with:
          script: |
            const fs = require('fs');
            const vision = fs.readFileSync('VISION.md', 'utf8');
            const contributing = fs.readFileSync('CONTRIBUTING.md', 'utf8');

            const isIssue = !!context.payload.issue;
            const item = context.payload.issue || context.payload.pull_request;
            const number = item.number;
            const title = item.title;
            const body = item.body || '';
            const type = isIssue ? 'issue' : 'pull_request';
            const existingLabels = (item.labels || []).map(l => l.name);

            const prompt = `You are a GitHub issue/PR triage bot for the SF project. Your job is to:
            1. Classify the ${type} with appropriate labels
            2. Detect if it violates project guidelines

            <vision>
            ${vision}
            </vision>

            <contributing>
            ${contributing}
            </contributing>

            <${type}>
            Title: ${title}
            Body: ${body}
            Existing labels: ${existingLabels.join(', ') || 'none'}
            </${type}>

            Available labels for classification (pick 1-3 that fit):
            - bug: Something isn't working
            - enhancement: New feature or request
            - documentation: Improvements or additions to documentation
            - performance: Performance improvement
            - refactor: Code restructuring without behavior change
            - tech-debt: Technical debt reduction
            - question: Further information is requested
            - good first issue: Good for newcomers
            - needs-info: Waiting for reporter information

            Available priority labels (pick exactly 1 if you can assess priority):
            - High Priority
            - Medium Priority
            - Low Priority

            Respond in this exact JSON format:
            {
              "labels": ["label1", "label2"],
              "aligned": true,
              "violation_type": null,
              "explanation": null
            }

            Set "aligned" to false if the ${type} clearly violates project guidelines. Violation types:
            - "enterprise-patterns": Enterprise patterns (DI containers, abstract factories, etc.)
            - "framework-swap": Rewriting working code in a different framework without measurable improvement
            - "cosmetic-refactor": Pure formatting/renaming churn with no user value
            - "complexity-without-value": Adds abstraction/indirection without user-visible improvement
            - "heavy-orchestration": Duplicates what agent infrastructure already provides
            - "missing-info": Issue is too vague to act on (no repro steps, no context)
            - "off-topic": Not related to SF at all
            - "security-in-public": Appears to report a security vulnerability publicly

            If aligned is false, provide a brief, polite explanation (2-3 sentences) of why this was flagged.

            Be generous in your assessment — only flag clear violations. Ambiguous cases should be marked as aligned.
            Do NOT flag issues/PRs that are legitimately reporting bugs or requesting features, even if they could be better written.`;

            if (!process.env.ANTHROPIC_API_KEY) {
              core.warning('Skipping AI triage because ANTHROPIC_API_KEY is not configured.');
              return;
            }

            let result;
            try {
              const response = await fetch('https://api.anthropic.com/v1/messages', {
                method: 'POST',
                headers: {
                  'x-api-key': process.env.ANTHROPIC_API_KEY,
                  'content-type': 'application/json',
                  'anthropic-version': '2023-06-01'
                },
                body: JSON.stringify({
                  model: 'claude-haiku-4-5-20251001',
                  max_tokens: 1024,
                  messages: [{ role: 'user', content: prompt }]
                }),
                signal: AbortSignal.timeout(20000)
              });

              if (!response.ok) {
                const err = await response.text();
                core.warning(`Skipping AI triage after Anthropic API error: ${response.status} ${err}`);
                return;
              }

              const data = await response.json();
              const text = data.content?.[0]?.text ?? '';

              // Extract JSON from response (handle markdown code blocks)
              const jsonMatch = text.match(/\{[\s\S]*\}/);
              if (!jsonMatch) {
                core.warning(`Skipping AI triage because the model response was not parseable JSON: ${text}`);
                return;
              }

              result = JSON.parse(jsonMatch[0]);
            } catch (e) {
              core.warning(`Skipping AI triage after unexpected failure: ${e.message}`);
              return;
            }
            core.info(`Triage result: ${JSON.stringify(result, null, 2)}`);

            // Apply labels
            const labelsToAdd = result.labels.filter(l => !existingLabels.includes(l));
            if (labelsToAdd.length > 0) {
              await github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: number,
                labels: labelsToAdd
              });
              core.info(`Added labels: ${labelsToAdd.join(', ')}`);
            }

            // Flag misaligned issues/PRs
            if (!result.aligned) {
              // Add needs-review label
              await github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: number,
                labels: ['needs-review']
              });

              const violationNames = {
                'enterprise-patterns': 'Enterprise patterns',
                'framework-swap': 'Framework swap',
                'cosmetic-refactor': 'Cosmetic refactor',
                'complexity-without-value': 'Complexity without user value',
                'heavy-orchestration': 'Heavy orchestration layer',
                'missing-info': 'Missing information',
                'off-topic': 'Off-topic',
                'security-in-public': 'Security report in public'
              };

              const securityNote = result.violation_type === 'security-in-public'
                ? `\n\n**If this is a security vulnerability, please delete this ${type} and use [GitHub\'s private vulnerability reporting](https://github.com/sf-build/SF/security/advisories/new) instead.** See [CONTRIBUTING.md](https://github.com/sf-build/SF/blob/main/CONTRIBUTING.md#security) for details.`
                : '';

              const comment = `👋 Thanks for opening this ${type}!

            This was automatically flagged for maintainer review.

            **Flag:** ${violationNames[result.violation_type] || result.violation_type}

            ${result.explanation}

            Please review our [VISION.md](https://github.com/sf-build/SF/blob/main/VISION.md) and [CONTRIBUTING.md](https://github.com/sf-build/SF/blob/main/CONTRIBUTING.md) for project guidelines.${securityNote}

            A maintainer will review this shortly. If you believe this was flagged in error, no action is needed — we'll take a look.

            ---
            *This is an automated triage. The maintainers make all final decisions.*`;

              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: number,
                body: comment
              });
              core.info(`Flagged as misaligned: ${result.violation_type}`);
            }
