# Copyright 2026 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import collections
import difflib
import math
import models


def _SelectEvenlySpaced(lst, n):
  length = len(lst)
  if n >= length:
    return lst

  step = length / n
  # Pick the element at the end of each calculated interval
  return [lst[math.ceil((i + 1) * step) - 1] for i in range(n)]


def SampleSymbols(candidates, changed_files=None):
  """Selects a representative subset of symbols to disassemble.

  Args:
    candidates: List of symbols to choose from. Should not contain any
        unchanged symbols.

  Returns:
    List of symbols.
  """
  if not candidates:
    return []

  selected = set()

  # Select at most 5 of each diff type.
  groups_by_status = candidates.GroupedByDiffStatus()
  for group in groups_by_status:
    sorted_symbols = sorted(group, key=lambda s: abs(s.size))
    selected.update(_SelectEvenlySpaced(sorted_symbols, 4))
    # Always take the 2 largest symbols, since they are generally more
    # interesting. The most largest is already added by _SelectEvenlySpaced().
    if len(sorted_symbols) > 4:
      selected.add(sorted_symbols[-2])

  # Make sure we have at least 8 symbols from changed files.
  if changed_files:
    changed_files = set(changed_files)
    contributed_files = {
      s.source_path for s in selected if s.source_path in changed_files
    }
    pool = [
      c
      for c in candidates
      if c.source_path in changed_files and c not in selected
    ]
    needed = 8 - sum(1 for s in selected if s.source_path in changed_files)

    for _ in range(needed):
      if not pool:
        break
      # Score: size + 100 bonus if file has not contributed yet
      pool.sort(
        key=lambda s: (
          abs(s.size) + (100 if s.source_path not in contributed_files else 0)
        ),
        reverse=True,
      )
      best = pool.pop(0)
      selected.add(best)
      contributed_files.add(best.source_path)

  return sorted(selected, key=lambda s: abs(s.size), reverse=True)


def CreateUnifiedDiff(name, before, after):
  """Helper to create a unified diff between before and after disassembly."""
  unified_diff = difflib.unified_diff(
    before, after, fromfile='before/' + name, tofile='after/' + name, n=10
  )
  return ''.join(unified_diff)
