// ci-generator/planu-steps.ts — Generate Planu-specific CI workflow YAML (SPEC-066 AC-02..07)
import { generatePlanuConfigYaml } from './planu-config.js';
// -- Constants ---------------------------------------------------------------
const DEFAULT_SPECS_DIR = 'docs/sdd/specs';
const WORKFLOW_PATH = '.github/workflows/planu-check.yml';
const COMMENT_MARKER = '<!-- planu-ci-report -->';
// -- Badge generation --------------------------------------------------------
/**
 * Generate the markdown snippet for adding a planu spec badge to README.
 */
export function generateBadgeMarkdown(owner, repo) {
    const url = `https://img.shields.io/endpoint?url=https://planu.dev/badge/${owner}/${repo}`;
    return `[![Planu Specs](${url})](https://planu.dev)`;
}
/**
 * Generate the badge update step YAML lines for the CI workflow.
 * Runs only on main/master push and creates a badge JSON endpoint.
 */
function buildBadgeStepLines(indent) {
    return [
        `${indent}- name: Generate spec badge`,
        `${indent}  if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'`,
        `${indent}  run: |`,
        `${indent}    TOTAL=$(echo "$PLANU_RESULTS" | grep -c "SPEC-" || echo "0")`,
        `${indent}    ISSUES=$(echo "$PLANU_RESULTS" | grep -c "FAIL" || echo "0")`,
        `${indent}    if [ "$ISSUES" = "0" ]; then`,
        `${indent}      COLOR="brightgreen"`,
        `${indent}      MSG="passing"`,
        `${indent}    else`,
        `${indent}      COLOR="red"`,
        `${indent}      MSG="$ISSUES issues"`,
        `${indent}    fi`,
        `${indent}    echo "{\\"schemaVersion\\":1,\\"label\\":\\"planu\\",\\"message\\":\\"$MSG\\",\\"color\\":\\"$COLOR\\"}" > planu-badge.json`,
        `${indent}  env:`,
        `${indent}    PLANU_RESULTS: \${{ steps.validate.outputs.results }}`,
    ];
}
// -- PR comment step ---------------------------------------------------------
function buildCommentStepLines(indent) {
    return [
        `${indent}- name: Post results to PR`,
        `${indent}  if: github.event_name == 'pull_request'`,
        `${indent}  uses: actions/github-script@v7`,
        `${indent}  with:`,
        `${indent}    script: |`,
        `${indent}      const marker = '${COMMENT_MARKER}';`,
        `${indent}      const body = marker + '\\n' + process.env.PLANU_COMMENT;`,
        `${indent}      const { data: comments } = await github.rest.issues.listComments({`,
        `${indent}        owner: context.repo.owner,`,
        `${indent}        repo: context.repo.repo,`,
        `${indent}        issue_number: context.issue.number,`,
        `${indent}      });`,
        `${indent}      const existing = comments.find(c => c.body && c.body.includes(marker));`,
        `${indent}      if (existing) {`,
        `${indent}        await github.rest.issues.updateComment({`,
        `${indent}          owner: context.repo.owner,`,
        `${indent}          repo: context.repo.repo,`,
        `${indent}          comment_id: existing.id,`,
        `${indent}          body,`,
        `${indent}        });`,
        `${indent}      } else {`,
        `${indent}        await github.rest.issues.createComment({`,
        `${indent}          owner: context.repo.owner,`,
        `${indent}          repo: context.repo.repo,`,
        `${indent}          issue_number: context.issue.number,`,
        `${indent}          body,`,
        `${indent}        });`,
        `${indent}      }`,
        `${indent}  env:`,
        `${indent}    PLANU_COMMENT: \${{ steps.validate.outputs.comment }}`,
    ];
}
// -- Severity / exit code step -----------------------------------------------
function buildSeverityStepLines(indent, threshold, failOnDrift) {
    if (!failOnDrift) {
        return [
            `${indent}- name: Check severity threshold`,
            `${indent}  run: echo "Drift detection is informational only (fail-on-drift=false)"`,
        ];
    }
    const thresholdCondition = threshold === 'critical'
        ? 'CRITICAL_COUNT=$(echo "$PLANU_RESULTS" | grep -c "CRITICAL" || echo "0") && [ "$CRITICAL_COUNT" -gt 0 ]'
        : threshold === 'warning'
            ? 'ISSUE_COUNT=$(echo "$PLANU_RESULTS" | grep -cE "(FAIL|DRIFT)" || echo "0") && [ "$ISSUE_COUNT" -gt 0 ]'
            : 'true';
    if (threshold === 'info') {
        return [
            `${indent}- name: Check severity threshold`,
            `${indent}  run: echo "Severity threshold is info — always passes"`,
        ];
    }
    return [
        `${indent}- name: Check severity threshold`,
        `${indent}  run: |`,
        `${indent}    if ${thresholdCondition}; then`,
        `${indent}      echo "::error::Planu specs have issues above ${threshold} threshold"`,
        `${indent}      exit 1`,
        `${indent}    fi`,
        `${indent}    echo "All specs within ${threshold} threshold"`,
        `${indent}  env:`,
        `${indent}    PLANU_RESULTS: \${{ steps.validate.outputs.results }}`,
    ];
}
// -- Drift detection step ----------------------------------------------------
function buildDriftStepLines(indent, specsDir) {
    return [
        `${indent}- name: Detect spec drift`,
        `${indent}  id: drift`,
        `${indent}  run: |`,
        `${indent}    CHANGED_FILES=$(git diff origin/main...HEAD --name-only 2>/dev/null || echo "")`,
        `${indent}    echo "Changed files:"`,
        `${indent}    echo "$CHANGED_FILES"`,
        `${indent}    DRIFT_FOUND="false"`,
        `${indent}    for spec_dir in ${specsDir}/*/; do`,
        `${indent}      if [ -f "$spec_dir/technical.md" ] || [ -f "$spec_dir/FICHA-TECNICA.md" ]; then`,
        `${indent}        TECH_FILE=$(ls "$spec_dir"technical.md "$spec_dir"FICHA-TECNICA.md 2>/dev/null | head -1)`,
        `${indent}        SPEC_FILES=$(grep -oE 'src/[a-zA-Z0-9_./-]+' "$TECH_FILE" 2>/dev/null || echo "")`,
        `${indent}        for sf in $SPEC_FILES; do`,
        `${indent}          if echo "$CHANGED_FILES" | grep -q "$sf"; then`,
        `${indent}            SPEC_NAME=$(basename "$spec_dir")`,
        `${indent}            echo "::warning::Drift detected: $sf modified, belongs to $SPEC_NAME"`,
        `${indent}            DRIFT_FOUND="true"`,
        `${indent}          fi`,
        `${indent}        done`,
        `${indent}      fi`,
        `${indent}    done`,
        `${indent}    echo "drift-detected=$DRIFT_FOUND" >> $GITHUB_OUTPUT`,
    ];
}
// -- Validate step -----------------------------------------------------------
function buildValidateStepLines(indent, specsDir) {
    return [
        `${indent}- name: Validate specs`,
        `${indent}  id: validate`,
        `${indent}  run: |`,
        `${indent}    RESULTS=""`,
        `${indent}    COMMENT="## Planu CI -- Reporte de Specs\\n\\n"`,
        `${indent}    COMMENT+="### Validacion de Specs\\n"`,
        `${indent}    COMMENT+="| Spec | Estado | Criterios |\\n"`,
        `${indent}    COMMENT+="|------|--------|-----------|\\n"`,
        `${indent}    TOTAL=0`,
        `${indent}    ISSUES=0`,
        `${indent}    for spec_dir in ${specsDir}/*/; do`,
        `${indent}      [ -d "$spec_dir" ] || continue`,
        `${indent}      SPEC_NAME=$(basename "$spec_dir")`,
        `${indent}      TOTAL=$((TOTAL + 1))`,
        `${indent}      SPEC_FILE=$(ls "$spec_dir"spec.md "$spec_dir"HU.md 2>/dev/null | head -1)`,
        `${indent}      if [ -z "$SPEC_FILE" ]; then`,
        `${indent}        RESULTS+="$SPEC_NAME FAIL missing-spec\\n"`,
        `${indent}        COMMENT+="| $SPEC_NAME | FAIL | missing spec.md |\\n"`,
        `${indent}        ISSUES=$((ISSUES + 1))`,
        `${indent}        continue`,
        `${indent}      fi`,
        `${indent}      CRITERIA=$(grep -c "^### AC-" "$SPEC_FILE" 2>/dev/null || echo "0")`,
        `${indent}      PROGRESS_FILE=$(ls "$spec_dir"progress.md "$spec_dir"PROGRESS.md 2>/dev/null | head -1)`,
        `${indent}      if [ -n "$PROGRESS_FILE" ]; then`,
        `${indent}        DONE=$(grep -c "Completado\\|Done\\|DONE" "$PROGRESS_FILE" 2>/dev/null || echo "0")`,
        `${indent}      else`,
        `${indent}        DONE=0`,
        `${indent}      fi`,
        `${indent}      if [ "$DONE" -ge "$CRITERIA" ] && [ "$CRITERIA" -gt 0 ]; then`,
        `${indent}        STATUS="PASS"`,
        `${indent}        RESULTS+="$SPEC_NAME PASS $DONE/$CRITERIA\\n"`,
        `${indent}      else`,
        `${indent}        STATUS="FAIL"`,
        `${indent}        ISSUES=$((ISSUES + 1))`,
        `${indent}        RESULTS+="$SPEC_NAME FAIL $DONE/$CRITERIA\\n"`,
        `${indent}      fi`,
        `${indent}      COMMENT+="| $SPEC_NAME | $STATUS | $DONE/$CRITERIA |\\n"`,
        `${indent}    done`,
        `${indent}    COMMENT+="\\n### Resumen\\n"`,
        `${indent}    COMMENT+="- Total specs verificadas: $TOTAL\\n"`,
        `${indent}    COMMENT+="- Specs con problemas: $ISSUES\\n"`,
        `${indent}    if [ "$TOTAL" = "0" ]; then`,
        `${indent}      COMMENT="## Planu CI -- Reporte de Specs\\n\\nTodas las specs estan en orden."`,
        `${indent}    fi`,
        `${indent}    echo "results<<EOF" >> $GITHUB_OUTPUT`,
        `${indent}    echo -e "$RESULTS" >> $GITHUB_OUTPUT`,
        `${indent}    echo "EOF" >> $GITHUB_OUTPUT`,
        `${indent}    echo "comment<<EOF" >> $GITHUB_OUTPUT`,
        `${indent}    echo -e "$COMMENT" >> $GITHUB_OUTPUT`,
        `${indent}    echo "EOF" >> $GITHUB_OUTPUT`,
        `${indent}    echo "issues=$ISSUES" >> $GITHUB_OUTPUT`,
    ];
}
// -- Workflow header builder --------------------------------------------------
function buildWorkflowHeader(postComment, workingDirectory) {
    const lines = [];
    lines.push('name: Planu Spec Check');
    lines.push('');
    lines.push('on:');
    lines.push('  push:');
    lines.push('    branches: [main, master]');
    lines.push('  pull_request:');
    lines.push('    branches: [main, master, develop]');
    lines.push('  workflow_dispatch:');
    lines.push('');
    if (postComment) {
        lines.push('permissions:');
        lines.push('  contents: read');
        lines.push('  pull-requests: write');
        lines.push('  issues: write');
        lines.push('');
    }
    lines.push('jobs:');
    lines.push('  planu-check:');
    lines.push('    name: Planu Spec Validation');
    lines.push('    runs-on: ubuntu-latest');
    if (workingDirectory !== '.') {
        lines.push(`    defaults:`);
        lines.push(`      run:`);
        lines.push(`        working-directory: ${workingDirectory}`);
    }
    return lines;
}
function buildConfigReadStep(ind) {
    return [
        `${ind}- name: Read .planu.yml config`,
        `${ind}  id: config`,
        `${ind}  run: |`,
        `${ind}    if [ -f ".planu.yml" ]; then`,
        `${ind}      echo "config-found=true" >> $GITHUB_OUTPUT`,
        `${ind}      echo "Planu config found at .planu.yml"`,
        `${ind}    else`,
        `${ind}      echo "config-found=false" >> $GITHUB_OUTPUT`,
        `${ind}      echo "No .planu.yml found, using defaults"`,
        `${ind}    fi`,
    ];
}
// -- Main generator ----------------------------------------------------------
/**
 * Generate a complete Planu CI check workflow with optional drift detection,
 * PR comments, badge generation, and configurable severity thresholds.
 */
export function generatePlanuCI(input) {
    const { specsDir = DEFAULT_SPECS_DIR, detectDrift = true, failOnDrift = true, severityThreshold = 'warning', postComment = true, badge = true, workingDirectory = '.', } = input;
    const features = ['validate'];
    const ind = '      ';
    const lines = buildWorkflowHeader(postComment, workingDirectory);
    lines.push('    steps:');
    lines.push(`${ind}- name: Checkout code`);
    lines.push(`${ind}  uses: actions/checkout@v4`);
    lines.push(`${ind}  with:`);
    lines.push(`${ind}    fetch-depth: 0`);
    lines.push('');
    features.push('planu-config');
    lines.push(...buildConfigReadStep(ind));
    lines.push('');
    const resolvedSpecsDir = workingDirectory === '.' ? specsDir : `${workingDirectory}/${specsDir}`;
    lines.push(...buildValidateStepLines(ind, resolvedSpecsDir));
    lines.push('');
    if (detectDrift) {
        features.push('detect-drift');
        lines.push(...buildDriftStepLines(ind, resolvedSpecsDir));
        lines.push('');
    }
    if (postComment) {
        features.push('pr-comment');
        lines.push(...buildCommentStepLines(ind));
        lines.push('');
    }
    if (badge) {
        features.push('badge');
        lines.push(...buildBadgeStepLines(ind));
        lines.push('');
    }
    if (failOnDrift) {
        features.push('fail-on-drift');
    }
    lines.push(...buildSeverityStepLines(ind, severityThreshold, failOnDrift));
    lines.push('');
    lines.push('    outputs:');
    lines.push('      drift-detected: ${{ steps.drift.outputs.drift-detected }}');
    lines.push('      issues: ${{ steps.validate.outputs.issues }}');
    const workflowYaml = lines.join('\n') + '\n';
    const planuConfigYaml = generatePlanuConfigYaml({
        specsDir,
        failOnDrift,
        severityThreshold,
        postComment,
        badge,
    });
    const badgeMarkdown = generateBadgeMarkdown('{owner}', '{repo}');
    return {
        workflowYaml,
        workflowPath: WORKFLOW_PATH,
        planuConfigYaml,
        badgeMarkdown,
        features,
    };
}
//# sourceMappingURL=planu-steps.js.map