# Copyright 2018 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd

"""FLT task to be manually triggered to convert launch issues."""
from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

import collections
import logging
import re
import settings
import time

from businesslogic import work_env
from framework import permissions
from framework import exceptions
from framework import jsonfeed
from proto import tracker_pb2
from tracker import template_helpers
from tracker import tracker_bizobj

PM_PREFIX = 'pm-'
TL_PREFIX = 'tl-'
TEST_PREFIX = 'test-'
UX_PREFIX = 'ux-'

PM_FIELD = 'pm'
TL_FIELD = 'tl'
TE_FIELD = 'te'
UX_FIELD = 'ux'
MTARGET_FIELD = 'm-target'
MAPPROVED_FIELD = 'm-approved'

CONVERSION_COMMENT = 'Automatic generating of FLT Launch data.'

BROWSER_APPROVALS_TO_LABELS = {
    'Chrome-Accessibility': 'Launch-Accessibility-',
    'Chrome-Leadership-Exp': 'Launch-Exp-Leadership-',
    'Chrome-Leadership-Full': 'Launch-Leadership-',
    'Chrome-Legal': 'Launch-Legal-',
    'Chrome-Privacy': 'Launch-Privacy-',
    'Chrome-Security': 'Launch-Security-',
    'Chrome-Test': 'Launch-Test-',
    'Chrome-UX': 'Launch-UI-',
    }

OS_APPROVALS_TO_LABELS = {
    'ChromeOS-Accessibility': 'Launch-Accessibility-',
    'ChromeOS-Leadership-Exp': 'Launch-Exp-Leadership-',
    'ChromeOS-Leadership-Full': 'Launch-Leadership-',
    'ChromeOS-Legal': 'Launch-Legal-',
    'ChromeOS-Privacy': 'Launch-Privacy-',
    'ChromeOS-Security': 'Launch-Security-',
    'ChromeOS-Test': 'Launch-Test-',
    'ChromeOS-UX': 'Launch-UI-',
    }

# 'NotReviewed' not included because this should be converted to
# the template approval's default value, eg NOT_SET OR NEEDS_REVIEW
VALUE_TO_STATUS = {
    'ReviewRequested': tracker_pb2.ApprovalStatus.REVIEW_REQUESTED,
    'NeedInfo': tracker_pb2.ApprovalStatus.NEED_INFO,
    'Yes': tracker_pb2.ApprovalStatus.APPROVED,
    'No': tracker_pb2.ApprovalStatus.NOT_APPROVED,
    'NA': tracker_pb2.ApprovalStatus.NA,
    # 'Started' is not a valid label value in the chromium project,
    # but for some reason, some labels have this value.
    'Started': tracker_pb2.ApprovalStatus.REVIEW_STARTED,
}

# This works in the Browser and OS process because
# BROWSER_APPROVALS_TO_LABELS and OS_APPROVALS_TO_LABELS have the same values.
# Adding '^' before each label prefix to ensure Blah-Launch-UI-Yes is ignored
REVIEW_LABELS_RE = re.compile('^' + '|^'.join(
    list(OS_APPROVALS_TO_LABELS.values())))

# Maps template phases to channel names in 'Launch-M-Target-80-[Channel]' labels
BROWSER_PHASE_MAP = {
    'beta': 'beta',
    'stable': 'stable',
    'stable-full': 'stable',
    'stable-exp': 'stable-exp',
    }

PHASE_PAT = '$|'.join(list(BROWSER_PHASE_MAP.values()))
# Matches launch milestone labels, eg. Launch-M-Target-70-Stable-Exp
BROWSER_M_LABELS_RE = re.compile(
    r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-'
    r'(?P<channel>%s$)' % PHASE_PAT,
    re.IGNORECASE)

OS_PHASE_MAP = {'feature freeze': '',
                 'branch': '',
                'stable': 'stable',
                'stable-full': 'stable',
                'stable-exp': 'stable-exp',}
# We only care about Launch-M-<type>-<m>-Stable|Stable-Exp labels for OS.
OS_M_LABELS_RE = re.compile(
    r'^Launch-M-(?P<type>Approved|Target)-(?P<m>\d\d)-'
    r'(?P<channel>Stable$|Stable-Exp$)', re.IGNORECASE)

CAN = 2  # Query for open issues only
# Ensure empty group_by_spec and sort_spec so issues are sorted by 'ID'.
GROUP_BY_SPEC = ''
SORT_SPEC = ''

CONVERT_NUM = 20
CONVERT_START = 0
VERIFY_NUM = 400

# Queries
QUERY_MAP = {
    'default':
    'Type=Launch Rollout-Type=Default OS=Windows,Mac,Linux,Android,iOS',
    'finch': 'Type=Launch Rollout-Type=Finch OS=Windows,Mac,Linux,Android,iOS',
    'os': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS'
    ' Rollout-Type=Default',
    'os-finch': 'Type=Launch OS=Chrome -OS=Windows,Mac,Linux,Android,iOS'
    ' Rollout-Type=Finch'}

TEMPLATE_MAP = {
    'default': 'Chrome Launch - Default',
    'finch': 'Chrome Launch - Experimental',
    'os': 'Chrome OS Launch - Default',
    'os-finch': 'Chrome OS Launch - Experimental',
}

ProjectInfo = collections.namedtuple(
    'ProjectInfo', 'config, q, approval_values, phases, '
    'pm_fid, tl_fid, te_fid, ux_fid, m_target_id, m_approved_id, '
    'phase_map, approvals_to_labels, labels_re')


class FLTConvertTask(jsonfeed.FlaskInternalTask):
  """FLTConvert converts current Type=Launch issues into Type=FLT-Launch."""

  def AssertBasePermission(self, mr):
    super(FLTConvertTask, self).AssertBasePermission(mr)
    if not mr.auth.user_pb.is_site_admin:
      raise permissions.PermissionException(
          'Only site admins may trigger conversion job')

  def UndoConversion(self, mr):
    with work_env.WorkEnv(mr, self.services) as we:
      pipeline = we.ListIssues(
          'Type=FLT-Launch FLT=Conversion', ['chromium'], mr.auth.user_id,
          CONVERT_NUM, CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)

    project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
    config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
    pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id
    tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id
    te_id = tracker_bizobj.FindFieldDef('TE', config).field_id
    ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id
    for possible_stale_issue in pipeline.visible_results:
      issue = self.services.issue.GetIssue(
          mr.cnxn, possible_stale_issue.issue_id, use_cache=False)

      issue.approval_values = []
      issue.phases = []
      issue.field_values = [fv for fv in issue.field_values
                            if fv.phase_id is None]
      issue.field_values = [fv for fv in issue.field_values
                            if fv.field_id not in
                            [pm_id, tl_id, te_id, ux_id]]
      issue.labels.remove('Type-FLT-Launch')
      issue.labels.remove('FLT-Conversion')
      issue.labels.append('Type-Launch')

      self.services.issue._UpdateIssuesApprovals(mr.cnxn, issue)
      self.services.issue.UpdateIssue(mr.cnxn, issue)
    return {'deleting': [issue.local_id for issue in pipeline.visible_results],
            'num': len(pipeline.visible_results),
    }

  def VerifyConversion(self, mr):
    """Verify that all FLT-Conversion issues were converted correctly."""
    with work_env.WorkEnv(mr, self.services) as we:
      pipeline = we.ListIssues(
          'FLT=Conversion', ['chromium'], mr.auth.user_id, VERIFY_NUM,
          CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)

    project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
    config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
    browser_approval_names = {fd.field_id: fd.field_name for fd
                              in config.field_defs if fd.field_name in
                              BROWSER_APPROVALS_TO_LABELS.keys()}
    os_approval_names = {fd.field_id: fd.field_name for fd in config.field_defs
                         if (fd.field_name in OS_APPROVALS_TO_LABELS.keys())
                         or fd.field_name == 'ChromeOS-Enterprise'}
    pm_id = tracker_bizobj.FindFieldDef('PM', config).field_id
    tl_id = tracker_bizobj.FindFieldDef('TL', config).field_id
    te_id = tracker_bizobj.FindFieldDef('TE', config).field_id
    ux_id = tracker_bizobj.FindFieldDef('UX', config).field_id
    mapproved_id = tracker_bizobj.FindFieldDef('M-Approved', config).field_id
    mtarget_id = tracker_bizobj.FindFieldDef('M-Target', config).field_id

    problems = []
    for possible_stale_issue in pipeline.allowed_results:
      issue = self.services.issue.GetIssue(
          mr.cnxn, possible_stale_issue.issue_id, use_cache=False)
      # Check correct template used
      approval_names = browser_approval_names
      approvals_to_labels = BROWSER_APPROVALS_TO_LABELS
      m_labels_re = BROWSER_M_LABELS_RE
      label_channel_to_phase_id = {
          phase.name.lower(): phase.phase_id for phase in issue.phases}
      if [l for l in issue.labels if l.startswith('OS-')] == ['OS-Chrome']:
        approval_names = os_approval_names
        m_labels_re = OS_M_LABELS_RE
        approvals_to_labels = OS_APPROVALS_TO_LABELS
        # OS default launch
        if 'Rollout-Type-Default' in issue.labels:
          if not all(phase.name in ['Feature Freeze', 'Branch', 'Stable']
                     for phase in issue.phases):
            problems.append((
                issue.local_id, 'incorrect phases for OS default launch.'))
        # OS finch launch
        elif 'Rollout-Type-Finch' in issue.labels:
          if not all(phase.name in (
              'Feature Freeze', 'Branch', 'Stable-Exp', 'Stable-Full')
                     for phase in issue.phases):
            problems.append((
                issue.local_id, 'incorrect phases for OS finch launch.'))
        else:
          problems.append((
              issue.local_id,
              'no rollout-type; should not have been converted'))
      # Browser default launch
      elif 'Rollout-Type-Default' in issue.labels:
        if not all(phase.name.lower() in ['beta', 'stable']
                   for phase in issue.phases):
          problems.append((
              issue.local_id, 'incorrect phases for Default rollout'))
      # Browser finch launch
      elif 'Rollout-Type-Finch' in issue.labels:
        if not all(phase.name.lower() in ['beta', 'stable-exp', 'stable-full']
                   for phase in issue.phases):
          problems.append((
              issue.local_id, 'incorrect phases for Finch rollout'))
      else:
        problems.append((
            issue.local_id,
            'no rollout-type; should not have been converted'))

      # Check approval_values
      for av in issue.approval_values:
        name = approval_names.get(av.approval_id)
        if name == 'ChromeOS-Enterprise':
          if av.status != tracker_pb2.ApprovalStatus.NEEDS_REVIEW:
            problems.append((issue.local_id, 'bad ChromeOS-Enterprise status'))
          continue
        label_pre = approvals_to_labels.get(name)
        if not label_pre:
          # either name was None or not found in APPROVALS_TO_LABELS
          problems.append((issue.local_id, 'approval %s not recognized' % name))
          continue
        label_value = next((l[len(label_pre):] for l in issue.labels
                            if l.startswith(label_pre)), None)
        if (not label_value or label_value == 'NotReviewed') and av.status in [
            tracker_pb2.ApprovalStatus.NOT_SET,
            tracker_pb2.ApprovalStatus.NEEDS_REVIEW]:
          continue
        if av.status is VALUE_TO_STATUS.get(label_value):
          continue
        # neither of the above ifs passed
        problems.append((issue.local_id,
                         'approval %s has status %r for label value %s' % (
                             name, av.status.name, label_value)))

      # Check people field_values
      expected_people_fvs = self.ConvertPeopleLabels(
          mr, issue.labels, pm_id, tl_id, te_id, ux_id)
      for people_fv in expected_people_fvs:
        if people_fv not in issue.field_values:
          if people_fv.field_id == tl_id:
            role = 'TL'
          elif people_fv.field_id == pm_id:
            role = 'PM'
          elif people_fv.field_id == ux_id:
            role = 'UX'
          else:
            role = 'TE'
          problems.append((issue.local_id, 'missing a field for %s' % role))

      # Check M phase field_values
      for label in issue.labels:
        match = re.match(m_labels_re, label)
        if match:
          channel = match.group('channel')
          if (channel.lower() == 'stable-exp'
              and 'Rollout-Type-Default' in issue.labels):
            # ignore stable-exp for default rollouts.
            continue
          milestone = match.group('m')
          m_type = match.group('type')
          m_id = mapproved_id if m_type == 'Approved' else mtarget_id
          phase_id = label_channel_to_phase_id.get(
              channel.lower(), label_channel_to_phase_id.get('stable-full'))
          if not next((
              fv for fv in issue.field_values
              if fv.phase_id == phase_id and fv.field_id == m_id and
              fv.int_value == int(milestone)), None):
            problems.append((
                issue.local_id, 'no phase field for label %s' % label))

    return {
        'problems found': ['issue %d: %s' % problem for problem in problems],
        'issues verified': ['issue %d' % issue.local_id for
                            issue in pipeline.allowed_results],
        'num': len(pipeline.allowed_results),
    }

  def HandleRequest(self, mr):
    """Convert Type=Launch issues to new Type=FLT-Launch issues."""
    launch = mr.GetParam('launch')
    if launch == 'delete':
      return self.UndoConversion(mr)
    if launch == 'verify':
      return self.VerifyConversion(mr)
    project_info = self.FetchAndAssertProjectInfo(mr)

    # Search for issues:
    with work_env.WorkEnv(mr, self.services) as we:
      pipeline = we.ListIssues(
          project_info.q, ['chromium'], mr.auth.user_id, CONVERT_NUM,
          CONVERT_START, 2, GROUP_BY_SPEC, SORT_SPEC, False)

    # Convert issues:
    for possible_stale_issue in pipeline.visible_results:
      # Note: These approval values and phases from templates will be used
      # and modified to create approval values and phases for each issue.
      # We need to create copies for each issue so changes are not carried
      # over to the conversion of the next issue in the loop.
      template_avs = self.CreateApprovalCopies(project_info.approval_values)
      template_phases = self.CreatePhasesCopies(project_info.phases)
      issue = self.services.issue.GetIssue(
          mr.cnxn, possible_stale_issue.issue_id, use_cache=False)
      new_approvals = ConvertLaunchLabels(
          issue.labels, template_avs,
          project_info.config.field_defs, project_info.approvals_to_labels)
      m_fvs = ConvertMLabels(
          issue.labels, template_phases,
          project_info.m_target_id, project_info.m_approved_id,
          project_info.labels_re, project_info.phase_map)
      people_fvs = self.ConvertPeopleLabels(
          mr, issue.labels,
          project_info.pm_fid, project_info.tl_fid, project_info.te_fid,
          project_info.ux_fid)
      amendments = self.ExecuteIssueChanges(
          project_info.config, issue, new_approvals,
          template_phases, m_fvs + people_fvs)
      logging.info(amendments)

    return {
        'converted_issues': [
            issue.local_id for issue in pipeline.visible_results],
        'num': len(pipeline.visible_results),
        }

  def CreateApprovalCopies(self, avs):
    return [
      tracker_pb2.ApprovalValue(
          approval_id=av.approval_id,
          status=av.status,
          setter_id=av.setter_id,
          set_on=av.set_on,
          phase_id=av.phase_id) for av in avs
    ]

  def CreatePhasesCopies(self, phases):
    return [
      tracker_pb2.Phase(
          phase_id=phase.phase_id,
          name=phase.name,
          rank=phase.rank) for phase in phases
        ]

  def FetchAndAssertProjectInfo(self, mr):
    # Get request details
    launch = mr.GetParam('launch')
    logging.info(launch)
    q = QUERY_MAP.get(launch)
    template_name = TEMPLATE_MAP.get(launch)
    assert q and template_name, 'bad launch type: %s' % launch

    phase_map = (
        OS_PHASE_MAP if launch in ['os', 'os-finch'] else BROWSER_PHASE_MAP)
    approvals_to_labels = (
        OS_APPROVALS_TO_LABELS if launch in ['os', 'os-finch']
        else BROWSER_APPROVALS_TO_LABELS)
    m_labels_re = (
        OS_M_LABELS_RE if launch in ['os', 'os-finch'] else BROWSER_M_LABELS_RE)

    # Get project, config, template, assert template in project
    project = self.services.project.GetProjectByName(mr.cnxn, 'chromium')
    config = self.services.config.GetProjectConfig(mr.cnxn, project.project_id)
    template = self.services.template.GetTemplateByName(
        mr.cnxn, template_name, project.project_id)
    assert template, 'template %s not found in chromium project' % template_name

    # Get template approval_values/phases and assert they are expected
    approval_values, phases = template_helpers.FilterApprovalsAndPhases(
        template.approval_values, template.phases, config)
    assert approval_values and phases, (
        'no approvals or phases in %s' % template_name)
    assert all(phase.name.lower() in list(
        phase_map.keys()) for phase in phases), (
          'one or more phases not recognized')
    if launch in ['finch', 'os', 'os-finch']:
      assert all(
          av.status is tracker_pb2.ApprovalStatus.NEEDS_REVIEW
          for av in approval_values
      ), '%s template not set up correctly' % launch

    approval_fds = {fd.field_id: fd.field_name for fd in config.field_defs
                    if fd.field_type is tracker_pb2.FieldTypes.APPROVAL_TYPE}
    assert all(
        approval_fds.get(av.approval_id) in list(approvals_to_labels.keys())
        for av in approval_values
        if approval_fds.get(av.approval_id) != 'ChromeOS-Enterprise'), (
            'one or more approvals not recognized')
    approval_def_ids = [ad.approval_id for ad in config.approval_defs]
    assert all(av.approval_id in approval_def_ids for av in approval_values), (
        'one or more approvals not in config.approval_defs')

    # Get relevant USER_TYPE FieldDef ids and assert they exist
    user_fds = {fd.field_name.lower(): fd.field_id for fd in config.field_defs
                    if fd.field_type is tracker_pb2.FieldTypes.USER_TYPE}
    logging.info('project USER_TYPE FieldDefs: %s' % user_fds)
    pm_fid = user_fds.get(PM_FIELD)
    assert pm_fid, 'project has no FieldDef %s' % PM_FIELD
    tl_fid = user_fds.get(TL_FIELD)
    assert tl_fid, 'project has no FieldDef %s' % TL_FIELD
    te_fid = user_fds.get(TE_FIELD)
    assert te_fid, 'project has no FieldDef %s' % TE_FIELD
    ux_fid = user_fds.get(UX_FIELD)
    assert ux_fid, 'project has no FieldDef %s' % UX_FIELD

    # Get relevant M Phase INT_TYPE FieldDef ids and assert they exist
    phase_int_fds = {fd.field_name.lower(): fd.field_id
                     for fd in config.field_defs
                     if fd.field_type is tracker_pb2.FieldTypes.INT_TYPE
                     and fd.is_phase_field and fd.is_multivalued}
    logging.info(
        'project Phase INT_TYPE multivalued FieldDefs: %s' % phase_int_fds)
    m_target_id = phase_int_fds.get(MTARGET_FIELD)
    assert m_target_id, 'project has no FieldDef %s' % MTARGET_FIELD
    m_approved_id = phase_int_fds.get(MAPPROVED_FIELD)
    assert m_approved_id, 'project has no FieldDef %s' % MAPPROVED_FIELD

    return ProjectInfo(config, q, approval_values, phases, pm_fid, tl_fid,
                       te_fid, ux_fid, m_target_id, m_approved_id, phase_map,
                       approvals_to_labels, m_labels_re)

  # TODO(jojwang): mr needs to be passed in as arg and
  # all self.mr should be changed to mr
  def ExecuteIssueChanges(self, config, issue, new_approvals, phases, new_fvs):
    # Apply Approval and phase changes
    approval_defs_by_id = {ad.approval_id: ad for ad in config.approval_defs}
    for av in new_approvals:
      ad = approval_defs_by_id.get(av.approval_id)
      if ad:
        av.approver_ids = ad.approver_ids
        survey = ''
        if ad.survey:
          questions = ad.survey.split('\n')
          survey = '\n'.join(['<b>' + q + '</b>' for q in questions])
        self.services.issue.InsertComment(
            self.mr.cnxn, tracker_pb2.IssueComment(
                issue_id=issue.issue_id, project_id=issue.project_id,
                user_id=self.mr.auth.user_id, content=survey,
                is_description=True, approval_id=av.approval_id,
                timestamp=int(time.time())))
      else:
        logging.info(
            'ERROR: ApprovalDef %r for ApprovalValue %r not valid', ad, av)
    issue.approval_values = new_approvals
    self.services.issue._UpdateIssuesApprovals(self.mr.cnxn, issue)

    # Apply field value changes
    issue.phases = phases
    delta = tracker_bizobj.MakeIssueDelta(
        None, None, [], [], [], [], ['Type-FLT-Launch', 'FLT-Conversion'],
        ['Type-Launch'], new_fvs, [], [], [], [], [], [], None, None)
    amendments, _ = self.services.issue.DeltaUpdateIssue(
        self.mr.cnxn, self.services, self.mr.auth.user_id, issue.project_id,
        config, issue, delta, comment=CONVERSION_COMMENT)

    return amendments

  def ConvertPeopleLabels(
      self, mr, labels, pm_field_id, tl_field_id, te_field_id, ux_field_id):
    field_values = []
    pm_ldap, tl_ldap, test_ldaps, ux_ldaps = ExtractLabelLDAPs(labels)

    pm_fv = self.CreateUserFieldValue(mr, pm_ldap, pm_field_id)
    if pm_fv:
      field_values.append(pm_fv)

    tl_fv = self.CreateUserFieldValue(mr, tl_ldap, tl_field_id)
    if tl_fv:
      field_values.append(tl_fv)

    for test_ldap in test_ldaps:
      te_fv = self.CreateUserFieldValue(mr, test_ldap, te_field_id)
      if te_fv:
        field_values.append(te_fv)

    for ux_ldap in ux_ldaps:
      ux_fv = self.CreateUserFieldValue(mr, ux_ldap, ux_field_id)
      if ux_fv:
        field_values.append(ux_fv)
    return field_values

  def CreateUserFieldValue(self, mr, ldap, field_id):
    if ldap is None:
      return None
    try:
      user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@chromium.org')
    except exceptions.NoSuchUserException:
      try:
        user_id = self.services.user.LookupUserID(mr.cnxn, ldap+'@google.com')
      except exceptions.NoSuchUserException:
        logging.info('No chromium.org or google.com accound found for %s', ldap)
        return None
    return tracker_bizobj.MakeFieldValue(
        field_id, None, None, user_id, None, None, False)

  def PostFLTConvertTask(self, **kwargs):
    return self.handler(**kwargs)


def ConvertMLabels(
    labels, phases, m_target_id, m_approved_id, labels_re, phase_map):
  field_values = []
  for label in labels:
    match = re.match(labels_re, label)
    if match:
      milestone = match.group('m')
      m_type = match.group('type')
      channel = match.group('channel')
      for phase in phases:
        # We know get(phase) will return something because
        # we're checking before ConvertMLabels, that all phases
        # exist in BROWSER_PHASE_MAP or OS_PHASE_MAP
        if phase_map.get(phase.name.lower()) == channel.lower():
          field_id = m_target_id if (
              m_type.lower() == 'target') else m_approved_id
          field_values.append(tracker_bizobj.MakeFieldValue(
              field_id, int(milestone), None, None, None, None, False,
              phase_id=phase.phase_id))
          break  # exit phase loop if match is found.
  return field_values


def ConvertLaunchLabels(labels, approvals, project_fds, approvals_to_labels):
  """Converts 'Launch-[Review]' values into statuses for given approvals."""
  label_values = {}
  for label in labels:
    launch_match = REVIEW_LABELS_RE.match(label)
    if launch_match:
      prefix = launch_match.group()
      value = label[len(prefix):]  # returns 'Yes' from 'Launch-UI-Yes'
      label_values[prefix] = value

  field_names_dict = {fd.field_id: fd.field_name for fd in project_fds}
  for approval in approvals:
    approval_name = field_names_dict.get(approval.approval_id, '')
    old_prefix = approvals_to_labels.get(approval_name)
    label_value = label_values.get(old_prefix, '')
    # if label_value not found in VALUE_TO_STATUS, use current status.
    approval.status = VALUE_TO_STATUS.get(label_value, approval.status)

  return approvals


def ExtractLabelLDAPs(labels):
  """Extracts LDAPs from labels 'PM-', 'TL-', 'UX-', and 'test-'"""

  pm_ldap = None
  tl_ldap = None
  test_ldaps = []
  ux_ldaps = []
  for label in labels:
    label = label.lower()
    if label.startswith(PM_PREFIX):
      pm_ldap = label[len(PM_PREFIX):]
    elif label.startswith(TL_PREFIX):
      tl_ldap = label[len(TL_PREFIX):]
    elif label.startswith(TEST_PREFIX):
      ldap = label[len(TEST_PREFIX):]
      if ldap:
        test_ldaps.append(ldap)
    elif label.startswith(UX_PREFIX):
      ldap = label[len(UX_PREFIX):]
      if ldap:
        ux_ldaps.append(ldap)
  return pm_ldap, tl_ldap, test_ldaps, ux_ldaps
