module.exports = async ({ github, context, core, config }) => {
  const { org, projectNumber, fieldMapping, issueNumber } = config;

  const issue = await getIssue(github, core, context, issueNumber);
  if (!issue) {
    core.info("No issue payload found. Skipping.");
    return;
  }

  // プロジェクト情報の取得
  core.debug(`Fetching Project info... Org: ${org}, Number: ${projectNumber}`);
  const project = await getProject(github, org, projectNumber);
  if (!project) {
    throw new Error(`Project not found. Check ORG_NAME (${org}) and PROJECT_NUM (${projectNumber}).`);
  }

  // Issue に対応する Project Item を取得
  const targetItem = await getTargetProjectItem(github, issue, projectNumber);
  if (!targetItem) {
    core.info(`Issue is not in Project #${projectNumber}. Skipping update.`);
    return;
  }
  core.debug(`Found Item ID: ${targetItem.id}`);

  // フィールドの更新
  const fields = project.fields.nodes;

  // 優先課題ラベルがある場合は Track フィールドを更新
  if (issue.labels.some((label) => label.name === "priority issue")) {
    await syncFieldValue(github, core, project, targetItem.id, fields, "Track", "🔥優先課題");
  }

  for (const [issueLabel, fieldName] of Object.entries(fieldMapping)) {
    let value = extractValue(issue.body, issueLabel);

    if (!value || value === "_No response_") {
      continue;
    }

    await syncFieldValue(github, core, project, targetItem.id, fields, fieldName, value);
  }
};

/*
 * Issue 情報を取得
 * Payload に issue 情報があればそれを使用し、なければ手動トリガー用の issueNumber から取得
 */
const getIssue = async (github, core, context, issueNumber) => {
  if (context.payload?.issue) {
    return context.payload.issue;
  }

  if (issueNumber) {
    core.info(`Manual Trigger Detected: Fetching Issue #${issueNumber}...`);
    const { data } = await github.rest.issues.get({
      owner: context.repo.owner,
      repo: context.repo.repo,
      issue_number: issueNumber,
    });
    return data;
  }

  return null;
};

/*
 * プロジェクト情報を取得
 */
const getProject = async (github, org, projectNumber) => {
  const query = `
    query($org: String!, $number: Int!) {
      organization(login: $org) {
        projectV2(number: $number) {
          id
          fields(first: 20) {
            nodes {
              ... on ProjectV2Field { id name dataType }
              ... on ProjectV2SingleSelectField { id name dataType options { id name } }
            }
          }
        }
      }
    }
  `;
  const data = await github.graphql(query, { org, number: projectNumber });
  return data.organization?.projectV2;
};

/*
 * Issue に対応する Project Item を取得
 */
const getTargetProjectItem = async (github, issue, projectNumber) => {
  const findItemQuery = `
    query($id: ID!) {
      node(id: $id) {
        ... on Issue {
          projectItems(first: 10) {
            nodes { id project { number } }
          }
        }
      }
    }
  `;

  const findResult = await github.graphql(findItemQuery, { id: issue.node_id });
  const projectItems = findResult.node.projectItems.nodes;
  return projectItems.find((item) => item.project.number === projectNumber);
};

/*
 * 指定されたヘッダーに基づいて本文から値を抽出
 */
const extractValue = (body, header) => {
  const regex = new RegExp(`### ${header}\\s+([\\s\\S]*?)(?=\\n###|$)`);
  const match = body.match(regex);
  return match ? match[1].trim() : null;
};

/*
 * フィールドを検索して値を更新
 */
const syncFieldValue = async (github, core, project, itemId, fields, fieldName, value) => {
  const field = fields.find((f) => f.name === fieldName);
  if (!field) {
    core.warning(`Field "${fieldName}" not found in Project.`);
    return;
  }

  let valueObj = {};
  switch (field.dataType) {
    case "DATE":
      valueObj = { date: value };
      break;
    case "SINGLE_SELECT":
      const option = field.options.find((o) => o.name === value);
      valueObj = option ? { singleSelectOptionId: option.id } : {};
      break;
    case "TEXT":
    default:
      valueObj = { text: value };
      break;
  }
  if (Object.keys(valueObj).length < 1) {
    core.debug(`No valid value for field "${field.name}". Skipping update.`);
    return;
  }

  core.debug(`Updating [${field.name}] -> "${JSON.stringify(valueObj)}"`);
  const updateFieldQuery = `
    mutation($pid: ID!, $iid: ID!, $fid: ID!, $val: ProjectV2FieldValue!) {
        updateProjectV2ItemFieldValue(input: {projectId: $pid, itemId: $iid, fieldId: $fid, value: $val}) {
          projectV2Item { id }
        }
      }
    `;
  await github.graphql(updateFieldQuery, { pid: project.id, iid: itemId, fid: field.id, val: valueObj });
};
