/**
 * LGTM Command Processor
 *
 * Processes /lgtm comments on pull requests. Checks if the commenter has the
 * required reviewer role(s) based on CODEOWNERS.md, then adds the lgtm label
 * and posts a confirmation comment.
 *
 * @param {object} params
 * @param {object} params.core    - @actions/core
 * @param {object} params.github  - @actions/github Octokit instance
 * @param {object} params.context - GitHub Actions context
 * @param {object} params.fs      - Node.js fs module
 */
export default async function run({ core, github, context, fs }) {
  const organization = 'external-secrets';
  const lgtmLabelName = 'lgtm';

  const owner = context.repo.owner;
  const repo = context.repo.repo;

  // Fail fast if the LGTM label does not exist in the repository
  const repoLabels = await github.paginate(github.rest.issues.listLabelsForRepo, {
    owner, repo, per_page: 100
  });
  if (!repoLabels.some(l => l.name === lgtmLabelName)) {
    core.setFailed(`LGTM label "${lgtmLabelName}" does not exist in the repository. Please create it before using the LGTM workflow.`);
    return;
  }

  const commenter = context.payload.comment.user.login;
  const prNumber = context.payload.issue.number;

  // Parse CODEOWNERS.md file
  let codeownersContent;
  try {
    codeownersContent = fs.readFileSync('CODEOWNERS.md', 'utf8');
  } catch (error) {
    return;
  }

  // Extract role mappings from CODEOWNERS.md (including * pattern)
  const codeownerMappings = [];
  let wildcardRoles = [];
  codeownersContent.split('\n').forEach(line => {
    const trimmed = line.trim();
    if (!trimmed || trimmed.startsWith('#')) return;

    const match = trimmed.match(/^(\S+)\s+(.+)$/);
    if (match) {
      const [, pattern, roles] = match;
      const rolesList = roles.split(/\s+/).filter(r => r.startsWith('@'));

      if (pattern === '*') {
        wildcardRoles = rolesList;
      } else {
        codeownerMappings.push({
          pattern,
          roles: rolesList
        });
      }
    }
  });

  // Extract maintainer roles from wildcardRoles
  const maintainerRoles = wildcardRoles.map(role => role.replace(`@${organization}/`, ''));

  // Early check: if user is a maintainer, approve immediately
  let isMaintainer = false;

  for (const role of maintainerRoles) {
    try {
      const response = await github.rest.teams.getMembershipForUserInOrg({
        org: organization,
        team_slug: role,
        username: commenter
      });
      if (response.data.state === 'active') {
        isMaintainer = true;
        break;
      }
    } catch (error) {
      // User not in this team, continue checking others
    }
  }

  if (isMaintainer) {
    // Check if LGTM label already exists
    const labels = await github.rest.issues.listLabelsOnIssue({
      owner, repo, issue_number: prNumber
    });
    if (!labels.data.some(l => l.name === lgtmLabelName)) {
      await github.rest.issues.addLabels({
        owner, repo, issue_number: prNumber, labels: [lgtmLabelName]
      });
    }

    // Simple confirmation for maintainers (no role mentions)
    await github.rest.issues.createComment({
      owner, repo, issue_number: prNumber,
      body: `✅ LGTM by @${commenter} (maintainer)`
    });
    return;
  }

  // Get changed files in PR
  const filesResponse = await github.rest.pulls.listFiles({
    owner, repo, pull_number: prNumber
  });
  const changedFiles = filesResponse.data.map(f => f.filename);

  // Find all required reviewer roles for the changed files
  // This includes hierarchical matching (e.g., pkg/provider/ matches pkg/provider/aws/file.go)
  const requiredReviewerRoles = new Set();
  let hasFilesWithoutSpecificOwners = false;

  changedFiles.forEach(file => {
    let hasSpecificOwner = false;
    codeownerMappings.forEach(mapping => {
      const { pattern, roles } = mapping;
      // Match pattern (handle both exact matches and directory patterns)
      // This handles hierarchical matching where broader patterns can cover more specific paths
      if (file === pattern ||
          file.startsWith(pattern.endsWith('/') ? pattern : pattern + '/')) {
        roles.forEach(role => requiredReviewerRoles.add(role));
        hasSpecificOwner = true;
      }
    });

    if (!hasSpecificOwner) {
      hasFilesWithoutSpecificOwners = true;
    }
  });

  // For files that only match the wildcard pattern, add wildcard roles
  if (hasFilesWithoutSpecificOwners) {
    wildcardRoles.forEach(role => {
      requiredReviewerRoles.add(role);
    });
  }


  // Find commenter's matching reviewer roles
  const commenterReviewerRoles = new Set();
  for (const role of requiredReviewerRoles) {
    const roleSlug = role.replace(`@${organization}/`, '');
    
    try {
      const response = await github.rest.teams.getMembershipForUserInOrg({
        org: organization,
        team_slug: roleSlug,
        username: commenter
      });
      if (response.data.state === 'active') {
        commenterReviewerRoles.add(role);
      }
    } catch (error) {
      // User not in this role, continue checking others
    }
  }

  // Check if user has any required reviewer role
  if (commenterReviewerRoles.size === 0) {
    const rolesList = Array.from(requiredReviewerRoles).map(role => role.replace(`@${organization}/`, '')).join(', ');
    await github.rest.issues.createComment({
      owner, repo, issue_number: prNumber,
      body: `@${commenter} You must be a member of one of the required reviewer roles to use /lgtm.\n\nRequired roles for this PR: ${rolesList}`
    });
    return;
  }

  // Check if LGTM label already exists
  const labels = await github.rest.issues.listLabelsOnIssue({
    owner, repo, issue_number: prNumber
  });
  
  if (!labels.data.some(l => l.name === lgtmLabelName)) {
    await github.rest.issues.addLabels({
      owner, repo, issue_number: prNumber, labels: [lgtmLabelName]
    });
    // Added LGTM label
  }

  // Build confirmation message with role coverage info
  const mentionRoles = wildcardRoles.join(' ');
  let confirmationMessage = `${mentionRoles}\n\n✅ LGTM by @${commenter}`;

  // Check for truly uncovered roles using pattern hierarchy
  const uncoveredRoles = [];
  for (const requiredRole of requiredReviewerRoles) {
    let isCovered = commenterReviewerRoles.has(requiredRole);

    // If not directly covered, check if any commenter role has a pattern that covers this required role's pattern
    if (!isCovered) {
      // Find the patterns for this required role
      const requiredRolePatterns = codeownerMappings
        .filter(mapping => mapping.roles.includes(requiredRole))
        .map(mapping => mapping.pattern);

      // Check if any commenter role has a pattern that is a parent of any required role pattern
      for (const commenterRole of commenterReviewerRoles) {
        const commenterRolePatterns = codeownerMappings
          .filter(mapping => mapping.roles.includes(commenterRole))
          .map(mapping => mapping.pattern);

        // Check if any commenter pattern is a parent of any required pattern
        for (const commenterPattern of commenterRolePatterns) {
          for (const requiredPattern of requiredRolePatterns) {
            const commenterPath = commenterPattern.endsWith('/') ? commenterPattern : commenterPattern + '/';
            const requiredPath = requiredPattern.endsWith('/') ? requiredPattern : requiredPattern + '/';

            // If required pattern starts with commenter pattern, it's hierarchically covered
            if (requiredPath.startsWith(commenterPath) && commenterPath !== requiredPath) {
              isCovered = true;
              break;
            }
          }
          if (isCovered) break;
        }
        if (isCovered) break;
      }
    }

    if (!isCovered) {
      uncoveredRoles.push(requiredRole);
    }
  }

  // Show role coverage analysis
  confirmationMessage += `\n\n**Review Coverage:**`;

  if (uncoveredRoles.length > 0) {
    confirmationMessage += `\n- Commenter has roles:`;
    Array.from(commenterReviewerRoles).forEach(role => {
      const roleName = role.replace(`@${organization}/`, '');
      confirmationMessage += `\n  - ${roleName}`;
    });
    confirmationMessage += `\n- Required roles:`;
    Array.from(requiredReviewerRoles).forEach(role => {
      const roleName = role.replace(`@${organization}/`, '');
      confirmationMessage += `\n  - ${roleName}`;
    });
    confirmationMessage += `\n- ❌ Additional review may be needed by:`;
    uncoveredRoles.forEach(role => {
      const roleName = role.replace(`@${organization}/`, '');
      confirmationMessage += `\n  - ${roleName}`;
    });
  } else {
    confirmationMessage += `\n- ✅ All required roles covered`;
  }
  
  await github.rest.issues.createComment({
    owner, repo, issue_number: prNumber,
    body: confirmationMessage
  });
}
