# coding=utf-8
# Copyright 2022 Google LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""A module that removes promotional text from a field of a product.

This modules removes promotional text from a product field that matches patterns
found in the "promo_text_removal_optimizer_config_{lang}" config file.
It uses two methods to remove the text.
- Regular expression: Removes text that matches regular expressions in the
config.
- Exact match: Removes text that exactly matches text in the config.

It does not transform product data unless a config file for the given language
exists.
"""

import logging
import re
from typing import Any, Dict, Optional

import flask

from util import config_parser

_PROMO_TEXT_REMOVAL_OPTIMIZER_CONFIG_FILE_NAME = (
    'promo_text_removal_optimizer_config_{}')
_PROMO_TEXT_REMOVAL_OPTIMIZER_CONFIG_OVERRIDE_KEY = (
    'promo_text_removal_optimizer_config_override')

# Can be set outside a Flask context; otherwise, no regex patterns are used.
PROMO_TEXT_REMOVER_CONFIG = {
    'promotional_text_patterns_regex': [],
    'promotional_text_patterns_exact_match': []
}


class PromoTextRemover(object):
  """A class that removes text from a field of a product."""

  _language: Optional[str] = None
  _config: Optional[Dict[str, Any]] = None

  def __init__(self, language: str) -> None:
    """Initializes PromoTextRemover.

    Args:
      language: The configured language code.
    """
    super(PromoTextRemover, self).__init__()
    self._language = language

    # Checks if running on Flask.
    if flask.current_app:
      self._config = config_parser.get_config_contents(
          _PROMO_TEXT_REMOVAL_OPTIMIZER_CONFIG_OVERRIDE_KEY,
          _PROMO_TEXT_REMOVAL_OPTIMIZER_CONFIG_FILE_NAME.format(language))
    else:
      self._config = PROMO_TEXT_REMOVER_CONFIG

    self.promo_text_words = frozenset(
        self._config.get('promotional_text_patterns_exact_match', set()))
    self.promo_regex_patterns = self._config.get(
        'promotional_text_patterns_regex', [])

  def remove_text_from_field(self, product: Dict[str, Any],
                             field_key: str) -> None:
    """Removes text and regex patterns in the config file from a product field.

    Args:
      product: A product data.
      field_key: The target field key.
    """
    field_value = product.get(field_key)
    if not isinstance(field_value, str):
      logging.warning(
          'Failed to remove text because The value of %s is not a string',
          field_key)
      return

    field_value = self._remove_text_by_regex_patterns(field_value)
    field_value = self._remove_text_by_exact_match(field_value)
    field_value = _clean_up_value(field_value)
    product[field_key] = field_value

  def _remove_text_by_regex_patterns(self, target_text: str) -> str:
    """Removes regex patterns in the config file from target text.

    Args:
      target_text: The original text.

    Returns:
      A string generated by removing regex patterns from the target text.
    """
    regex_patterns = self._config.get('promotional_text_patterns_regex', [])
    for regex_pattern in regex_patterns:
      target_text = re.sub(regex_pattern, '', target_text, flags=re.I)
    target_text = ' '.join(target_text.split())
    return target_text

  def _remove_text_by_exact_match(self, target_text: str) -> str:
    """Removes text in the config file from target text.

    Args:
      target_text: The original text.

    Returns:
      A string generated by removing text from the target text.
    """
    text_list = self._config.get('promotional_text_patterns_exact_match', [])
    for text in text_list:
      target_text = target_text.replace(text, '')
    target_text = ' '.join(target_text.split())
    return target_text

  def remove_keywords_with_promo(self, keywords: [str]) -> {str}:
    """Remove elements from the list containing promo text.

    Args:
      keywords: List of strings containing or not promo text

    Returns:
      A set of strings containing no promo text
    """
    keywords_with_no_promo_text = set()

    for keyword in keywords:
      if keyword in self.promo_text_words:
        continue

      regex_pattern_found = False
      for regex_pattern in self.promo_regex_patterns:
        match = re.search(regex_pattern, keyword)
        if match:
          regex_pattern_found = True
          break

      if not regex_pattern_found:
        keywords_with_no_promo_text.add(keyword)

    return keywords_with_no_promo_text


def _clean_up_value(value: str) -> str:
  """Cleans up a field value."""
  return value.strip()
