const { MANAGED_LABELS } = require('./constants');
const {
  detectMergeBranch,
  detectComponentPlatforms,
  detectNewComponents,
  detectNewPlatforms,
  detectCoreChanges,
  detectPRSize,
  detectDashboardChanges,
  detectGitHubActionsChanges,
  detectCodeOwner,
  detectTests,
  detectPRTemplateCheckboxes,
  detectDeprecatedComponents,
  detectMaintainerAccess,
  detectRequirements
} = require('./detectors');
const { handleReviews, handleMaintainerAccessComment } = require('./reviews');
const { applyLabels, removeOldLabels } = require('./labels');

// Fetch API data
async function fetchApiData() {
  try {
    const response = await fetch('https://data.esphome.io/components.json');
    const componentsData = await response.json();
    return {
      targetPlatforms: componentsData.target_platforms || [],
      platformComponents: componentsData.platform_components || []
    };
  } catch (error) {
    console.log('Failed to fetch components data from API:', error.message);
    return { targetPlatforms: [], platformComponents: [] };
  }
}

module.exports = async ({ github, context }) => {
  // Environment variables
  const SMALL_PR_THRESHOLD = parseInt(process.env.SMALL_PR_THRESHOLD);
  const MEDIUM_PR_THRESHOLD = parseInt(process.env.MEDIUM_PR_THRESHOLD);
  const MAX_LABELS = parseInt(process.env.MAX_LABELS);
  const TOO_BIG_THRESHOLD = parseInt(process.env.TOO_BIG_THRESHOLD);
  const COMPONENT_LABEL_THRESHOLD = parseInt(process.env.COMPONENT_LABEL_THRESHOLD);

  // Global state
  const { owner, repo } = context.repo;
  const pr_number = context.issue.number;

  // Get current labels and PR data
  const { data: currentLabelsData } = await github.rest.issues.listLabelsOnIssue({
    owner,
    repo,
    issue_number: pr_number
  });
  const currentLabels = currentLabelsData.map(label => label.name);
  const managedLabels = currentLabels.filter(label =>
    label.startsWith('component: ') || MANAGED_LABELS.includes(label)
  );

  // Check for mega-PR early - if present, skip most automatic labeling
  const isMegaPR = currentLabels.includes('mega-pr');

  // Get all PR files with automatic pagination
  const prFiles = await github.paginate(
    github.rest.pulls.listFiles,
    {
      owner,
      repo,
      pull_number: pr_number
    }
  );

  // Calculate data from PR files
  const changedFiles = prFiles.map(file => file.filename);
  const totalAdditions = prFiles.reduce((sum, file) => sum + (file.additions || 0), 0);
  const totalDeletions = prFiles.reduce((sum, file) => sum + (file.deletions || 0), 0);
  const totalChanges = totalAdditions + totalDeletions;

  console.log('Current labels:', currentLabels.join(', '));
  console.log('Changed files:', changedFiles.length);
  console.log('Total changes:', totalChanges);
  if (isMegaPR) {
    console.log('Mega-PR detected - applying limited labeling logic');
  }

  // Fetch API data
  const apiData = await fetchApiData();
  const baseRef = context.payload.pull_request.base.ref;

  // Early exit for release and beta branches only
  if (baseRef === 'release' || baseRef === 'beta') {
    const branchLabels = await detectMergeBranch(context);
    const finalLabels = Array.from(branchLabels);

    console.log('Computed labels (merge branch only):', finalLabels.join(', '));

    // Apply labels
    await applyLabels(github, context, finalLabels);

    // Remove old managed labels
    await removeOldLabels(github, context, managedLabels, finalLabels);

    return;
  }

  // Run all strategies
  const [
    branchLabels,
    componentLabels,
    newComponentResult,
    newPlatformResult,
    coreLabels,
    sizeLabels,
    dashboardLabels,
    actionsLabels,
    codeOwnerLabels,
    testLabels,
    checkboxLabels,
    deprecatedResult,
    maintainerAccess
  ] = await Promise.all([
    detectMergeBranch(context),
    detectComponentPlatforms(changedFiles, apiData),
    detectNewComponents(github, context, prFiles),
    detectNewPlatforms(github, context, prFiles, apiData),
    detectCoreChanges(changedFiles),
    detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
    detectDashboardChanges(changedFiles),
    detectGitHubActionsChanges(changedFiles),
    detectCodeOwner(github, context, changedFiles),
    detectTests(changedFiles),
    detectPRTemplateCheckboxes(context),
    detectDeprecatedComponents(github, context, changedFiles),
    detectMaintainerAccess(context)
  ]);

  // Extract new-component / new-platform results
  const newComponentLabels = newComponentResult.labels;
  const newPlatformLabels = newPlatformResult.labels;
  // Eligible for needs-docs only if any newly added component or platform file
  // defines a top-level CONFIG_SCHEMA (i.e. is actually loadable from YAML).
  const hasYamlLoadable = newComponentResult.hasYamlLoadable || newPlatformResult.hasYamlLoadable;

  // Extract deprecated component info
  const deprecatedLabels = deprecatedResult.labels;
  const deprecatedInfo = deprecatedResult.deprecatedInfo;

  // Combine all labels
  const allLabels = new Set([
    ...branchLabels,
    ...componentLabels,
    ...newComponentLabels,
    ...newPlatformLabels,
    ...coreLabels,
    ...sizeLabels,
    ...dashboardLabels,
    ...actionsLabels,
    ...codeOwnerLabels,
    ...testLabels,
    ...checkboxLabels,
    ...deprecatedLabels
  ]);

  // Detect requirements based on all other labels
  const requirementLabels = await detectRequirements(allLabels, prFiles, context, hasYamlLoadable);
  for (const label of requirementLabels) {
    allLabels.add(label);
  }

  let finalLabels = Array.from(allLabels);

  // For mega-PRs, exclude component labels if there are too many
  if (isMegaPR) {
    const componentLabels = finalLabels.filter(label => label.startsWith('component: '));
    if (componentLabels.length > COMPONENT_LABEL_THRESHOLD) {
      finalLabels = finalLabels.filter(label => !label.startsWith('component: '));
      console.log(`Mega-PR detected - excluding ${componentLabels.length} component labels (threshold: ${COMPONENT_LABEL_THRESHOLD})`);
    }
  }

  // Handle too many labels (only for non-mega PRs)
  const tooManyLabels = finalLabels.length > MAX_LABELS;
  const originalLabelCount = finalLabels.length;

  if (tooManyLabels && !isMegaPR && !finalLabels.includes('too-big')) {
    finalLabels = ['too-big'];
  }

  console.log('Computed labels:', finalLabels.join(', '));

  // Handle reviews and org fork comment
  await Promise.all([
    handleReviews(github, context, finalLabels, originalLabelCount, deprecatedInfo, prFiles, totalAdditions, totalDeletions, MAX_LABELS, TOO_BIG_THRESHOLD),
    handleMaintainerAccessComment(github, context, maintainerAccess)
  ]);

  // Apply labels
  await applyLabels(github, context, finalLabels);

  // Remove old managed labels
  await removeOldLabels(github, context, managedLabels, finalLabels);
};
