name: Sync Project Fields

on:
  issues:
    types: [opened, labeled, unlabeled, reopened]

permissions: {}

jobs:
  sync-fields:
    name: Sync Type/Priority fields from labels
    runs-on: ubuntu-latest
    steps:
      - name: Sync fields
        uses: actions/github-script@v8
        with:
          github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}
          script: |
            const projectId = 'PVT_kwHOADH82s4BO2OT';
            const issueNodeId = context.payload.issue.node_id;
            const issueNumber = context.payload.issue.number;
            const labels = (context.payload.issue.labels || []).map(l => l.name.toLowerCase());

            const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));

            const getProjectItem = async () => {
              const data = await github.graphql(`
                query($nodeId: ID!) {
                  node(id: $nodeId) {
                    ... on Issue {
                      projectItems(first: 20) {
                        nodes {
                          id
                          project { id }
                        }
                      }
                    }
                  }
                }
              `, { nodeId: issueNodeId });

              const items = data.node?.projectItems?.nodes || [];
              return items.find(item => item.project.id === projectId) || null;
            };

            let projectItem = await getProjectItem();

            // add-to-project may run in parallel; retry briefly before giving up
            for (let i = 0; !projectItem && i < 6; i++) {
              console.log(`Issue #${issueNumber} not in project yet. Retry ${i + 1}/6...`);
              await sleep(10000);
              projectItem = await getProjectItem();
            }

            if (!projectItem) {
              console.log(`Issue #${issueNumber} is not in project board. Skipping field sync.`);
              return;
            }

            const fieldsData = await github.graphql(`
              query($projectId: ID!) {
                node(id: $projectId) {
                  ... on ProjectV2 {
                    fields(first: 50) {
                      nodes {
                        ... on ProjectV2SingleSelectField {
                          id
                          name
                          options {
                            id
                            name
                          }
                        }
                      }
                    }
                  }
                }
              }
            `, { projectId });

            const singleSelectFields = fieldsData.node?.fields?.nodes || [];
            const byName = new Map(singleSelectFields.map(f => [f.name, f]));

            const typeField = byName.get('Type');
            const priorityField = byName.get('Priority');

            if (!typeField && !priorityField) {
              console.log('Neither Type nor Priority field found. Nothing to update.');
              return;
            }

            const pickOptionId = (field, optionName) => {
              if (!field || !optionName) return null;
              const opt = (field.options || []).find(o => o.name.toLowerCase() === optionName.toLowerCase());
              return opt?.id || null;
            };

            let typeName = null;
            if (labels.includes('bug')) typeName = 'Bug';
            else if (labels.includes('enhancement')) typeName = 'Feature';
            else if (labels.includes('documentation')) typeName = 'Documentation';

            let priorityName = null;
            if (labels.includes('priority/high')) priorityName = 'High';
            else if (labels.includes('priority/low')) priorityName = 'Low';
            else if (labels.includes('priority/medium')) priorityName = 'Medium';
            else if (labels.includes('triage')) priorityName = 'Medium';

            const updates = [];
            const typeOptionId = pickOptionId(typeField, typeName);
            if (typeField && typeOptionId) {
              updates.push({ fieldId: typeField.id, optionId: typeOptionId, fieldName: 'Type', valueName: typeName });
            }

            const priorityOptionId = pickOptionId(priorityField, priorityName);
            if (priorityField && priorityOptionId) {
              updates.push({ fieldId: priorityField.id, optionId: priorityOptionId, fieldName: 'Priority', valueName: priorityName });
            }

            for (const update of updates) {
              await github.graphql(`
                mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
                  updateProjectV2ItemFieldValue(input: {
                    projectId: $projectId
                    itemId: $itemId
                    fieldId: $fieldId
                    value: { singleSelectOptionId: $optionId }
                  }) {
                    projectV2Item { id }
                  }
                }
              `, {
                projectId,
                itemId: projectItem.id,
                fieldId: update.fieldId,
                optionId: update.optionId
              });

              console.log(`Issue #${issueNumber}: set ${update.fieldName} = ${update.valueName}`);
            }

            if (updates.length === 0) {
              console.log(`Issue #${issueNumber}: no matching field updates for labels [${labels.join(', ')}]`);
            }
