name: Convert Docs to Issues

on:
  push:
    branches:
      - main
      - development
      - feature/**
    paths:
      - 'docs/issues/**/*.md'
      - '!docs/issues/created/**'
      - '!docs/issues/_TEMPLATE.md'
      - '!docs/issues/README.md'

  # Allow manual trigger
  workflow_dispatch:
    inputs:
      dry_run:
        description: 'Dry run (no issues created)'
        required: false
        default: false
        type: boolean
      file_path:
        description: 'Specific file to process (optional)'
        required: false
        type: string

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

env:
  NODE_VERSION: '24.12.0'

permissions:
  contents: write
  issues: write
  pull-requests: write

jobs:
  convert-docs:
    name: Convert Markdown to Issues
    runs-on: ubuntu-latest
    if: github.actor != 'github-actions[bot]'

    steps:
      - name: Checkout repository
        uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
        with:
          fetch-depth: 2

      - name: Set up Node.js
        uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Install dependencies
        run: npm install gray-matter

      - name: Detect changed files
        id: changes
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        with:
          script: |
            const fs = require('fs');
            const path = require('path');

            // Manual file specification
            const manualFile = '${{ github.event.inputs.file_path }}';
            if (manualFile) {
              if (fs.existsSync(manualFile)) {
                core.setOutput('files', JSON.stringify([manualFile]));
                return;
              } else {
                core.setFailed(`File not found: ${manualFile}`);
                return;
              }
            }

            // Get changed files from commit
            const { data: commit } = await github.rest.repos.getCommit({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: context.sha
            });

            const changedFiles = (commit.files || [])
              .filter(f => f.filename.startsWith('docs/issues/'))
              .filter(f => !f.filename.startsWith('docs/issues/created/'))
              .filter(f => !f.filename.includes('_TEMPLATE'))
              .filter(f => !f.filename.includes('README'))
              .filter(f => f.filename.endsWith('.md'))
              .filter(f => f.status !== 'removed')
              .map(f => f.filename);

            console.log('Changed issue files:', changedFiles);
            core.setOutput('files', JSON.stringify(changedFiles));

      - name: Process issue files
        id: process
        uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
        env:
          DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
        with:
          script: |
            const fs = require('fs');
            const path = require('path');
            const matter = require('gray-matter');

            const files = JSON.parse('${{ steps.changes.outputs.files }}');
            const isDryRun = process.env.DRY_RUN === 'true';
            const createdIssues = [];
            const errors = [];

            if (files.length === 0) {
              console.log('No issue files to process');
              core.setOutput('created_count', 0);
              core.setOutput('created_issues', '[]');
              core.setOutput('errors', '[]');
              return;
            }

            // Label color map
            const labelColors = {
              testing: 'BFD4F2',
              feature: 'A2EEEF',
              enhancement: '84B6EB',
              bug: 'D73A4A',
              documentation: '0075CA',
              backend: '1D76DB',
              frontend: '5EBEFF',
              security: 'EE0701',
              ui: '7057FF',
              caddy: '1F6FEB',
              'needs-triage': 'FBCA04',
              acl: 'C5DEF5',
              regression: 'D93F0B',
              'manual-testing': 'BFD4F2',
              'bulk-acl': '006B75',
              'error-handling': 'D93F0B',
              'ui-ux': '7057FF',
              integration: '0E8A16',
              performance: 'EDEDED',
              'cross-browser': '5319E7',
              plus: 'FFD700',
              beta: '0052CC',
              alpha: '5319E7',
              high: 'D93F0B',
              medium: 'FBCA04',
              low: '0E8A16',
              critical: 'B60205',
              architecture: '006B75',
              database: '006B75',
              'post-beta': '006B75'
            };

            // Helper: Ensure label exists
            async function ensureLabel(name) {
              try {
                await github.rest.issues.getLabel({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  name: name
                });
              } catch (e) {
                if (e.status === 404) {
                  await github.rest.issues.createLabel({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    name: name,
                    color: labelColors[name.toLowerCase()] || '666666'
                  });
                  console.log(`Created label: ${name}`);
                }
              }
            }

            // Helper: Parse markdown file
            function parseIssueFile(filePath) {
              const content = fs.readFileSync(filePath, 'utf8');
              const { data: frontmatter, content: body } = matter(content);

              // Extract title: frontmatter > first H1 > filename
              let title = frontmatter.title;
              if (!title) {
                const h1Match = body.match(/^#\s+(.+)$/m);
                title = h1Match ? h1Match[1] : path.basename(filePath, '.md').replace(/-/g, ' ');
              }

              // Build labels array
              const labels = [...(frontmatter.labels || [])];
              if (frontmatter.priority) labels.push(frontmatter.priority);
              if (frontmatter.type) labels.push(frontmatter.type);

              return {
                title,
                body: body.trim(),
                labels: [...new Set(labels)],
                assignees: frontmatter.assignees || [],
                milestone: frontmatter.milestone,
                parent_issue: frontmatter.parent_issue,
                create_sub_issues: frontmatter.create_sub_issues || false
              };
            }

            // Helper: Extract sub-issues from H2 sections
            function extractSubIssues(body, parentLabels) {
              const sections = [];
              const lines = body.split('\n');
              let currentSection = null;
              let currentBody = [];

              for (const line of lines) {
                const h2Match = line.match(/^##\s+(?:Sub-Issue\s*#?\d*:?\s*)?(.+)$/);
                if (h2Match) {
                  if (currentSection) {
                    sections.push({
                      title: currentSection,
                      body: currentBody.join('\n').trim(),
                      labels: [...parentLabels]
                    });
                  }
                  currentSection = h2Match[1].trim();
                  currentBody = [];
                } else if (currentSection) {
                  currentBody.push(line);
                }
              }

              if (currentSection) {
                sections.push({
                  title: currentSection,
                  body: currentBody.join('\n').trim(),
                  labels: [...parentLabels]
                });
              }

              return sections;
            }

            // Process each file
            for (const filePath of files) {
              console.log(`\nProcessing: ${filePath}`);

              try {
                const parsed = parseIssueFile(filePath);
                console.log(`  Title: ${parsed.title}`);
                console.log(`  Labels: ${parsed.labels.join(', ')}`);

                if (isDryRun) {
                  console.log('  [DRY RUN] Would create issue');
                  createdIssues.push({ file: filePath, title: parsed.title, dryRun: true });
                  continue;
                }

                // Ensure labels exist
                for (const label of parsed.labels) {
                  await ensureLabel(label);
                }

                // Create the main issue
                const issueBody = parsed.body +
                  `\n\n---\n*Auto-created from [${path.basename(filePath)}](https://github.com/${context.repo.owner}/${context.repo.repo}/blob/${context.sha}/${filePath})*`;

                const issueResponse = await github.rest.issues.create({
                  owner: context.repo.owner,
                  repo: context.repo.repo,
                  title: parsed.title,
                  body: issueBody,
                  labels: parsed.labels,
                  assignees: parsed.assignees
                });

                const issueNumber = issueResponse.data.number;
                console.log(`  Created issue #${issueNumber}`);

                // Handle sub-issues
                if (parsed.create_sub_issues) {
                  const subIssues = extractSubIssues(parsed.body, parsed.labels);
                  for (const sub of subIssues) {
                    for (const label of sub.labels) {
                      await ensureLabel(label);
                    }
                    const subResponse = await github.rest.issues.create({
                      owner: context.repo.owner,
                      repo: context.repo.repo,
                      title: `[${parsed.title}] ${sub.title}`,
                      body: sub.body + `\n\n---\n*Sub-issue of #${issueNumber}*`,
                      labels: sub.labels,
                      assignees: parsed.assignees
                    });
                    console.log(`    Created sub-issue #${subResponse.data.number}: ${sub.title}`);
                  }
                }

                // Link to parent issue if specified
                if (parsed.parent_issue) {
                  await github.rest.issues.createComment({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    issue_number: parsed.parent_issue,
                    body: `Sub-issue created: #${issueNumber}`
                  });
                }

                createdIssues.push({
                  file: filePath,
                  title: parsed.title,
                  issueNumber
                });

              } catch (error) {
                console.error(`  Error processing ${filePath}: ${error.message}`);
                errors.push({ file: filePath, error: error.message });
              }
            }

            core.setOutput('created_count', createdIssues.length);
            core.setOutput('created_issues', JSON.stringify(createdIssues));
            core.setOutput('errors', JSON.stringify(errors));

            if (errors.length > 0) {
              core.warning(`${errors.length} file(s) had errors`);
            }

      - name: Move processed files
        if: steps.process.outputs.created_count != '0' && github.event.inputs.dry_run != 'true'
        run: |
          mkdir -p docs/issues/created
          CREATED_ISSUES='${{ steps.process.outputs.created_issues }}'
          echo "$CREATED_ISSUES" | jq -r '.[].file' | while read file; do
            if [ -f "$file" ] && [ ! -z "$file" ]; then
              filename=$(basename "$file")
              timestamp=$(date +%Y%m%d)
              mv "$file" "docs/issues/created/${timestamp}-${filename}"
              echo "Moved: $file -> docs/issues/created/${timestamp}-${filename}"
            fi
          done

      - name: Commit moved files
        if: steps.process.outputs.created_count != '0' && github.event.inputs.dry_run != 'true'
        run: |
          git config --local user.email "github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git add docs/issues/
          # Removed [skip ci] to allow CI checks to run on PRs
          # Infinite loop protection: path filter excludes docs/issues/created/** AND github.actor guard prevents bot loops
          git diff --staged --quiet || git commit -m "chore: move processed issue files to created/"
          git push

      - name: Summary
        if: always()
        run: |
          echo "## Docs to Issues Summary" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          CREATED='${{ steps.process.outputs.created_issues }}'
          ERRORS='${{ steps.process.outputs.errors }}'
          DRY_RUN='${{ github.event.inputs.dry_run }}'

          if [ "$DRY_RUN" = "true" ]; then
            echo "🔍 **Dry Run Mode** - No issues were actually created" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
          fi

          echo "### Created Issues" >> $GITHUB_STEP_SUMMARY
          if [ -n "$CREATED" ] && [ "$CREATED" != "[]" ] && [ "$CREATED" != "null" ]; then
            echo "$CREATED" | jq -r '.[] | "- \(.title) (#\(.issueNumber // "dry-run"))"' >> $GITHUB_STEP_SUMMARY || echo "_Parse error_" >> $GITHUB_STEP_SUMMARY
          else
            echo "_No issues created_" >> $GITHUB_STEP_SUMMARY
          fi

          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Errors" >> $GITHUB_STEP_SUMMARY
          if [ -n "$ERRORS" ] && [ "$ERRORS" != "[]" ] && [ "$ERRORS" != "null" ]; then
            echo "$ERRORS" | jq -r '.[] | "- ❌ \(.file): \(.error)"' >> $GITHUB_STEP_SUMMARY || echo "_Parse error_" >> $GITHUB_STEP_SUMMARY
          else
            echo "_No errors_" >> $GITHUB_STEP_SUMMARY
          fi
