# Copyright 2023 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
#
#     https://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.
"""Module for translating exclusion rule into specifications."""
from __future__ import annotations

import dataclasses
import re
from collections.abc import Sequence


@dataclasses.dataclass(frozen=True)
class Rule:
    """Holds information on exclusion rule."""
    exclusion_type: str
    exclusion_rule: str


def generate_rules(raw_rules: str | Sequence[str]) -> list[list[Rule]] | None:
    """Convert raw string rules into list of Rules.

    Rule as raw string can be expressed in an explicit
    (`exclusion_type:exclusion_rule`), implicit (`exclusion_rule`) format.

    Rules in each of these formats can be represented as sequence or a single
    string. And-rules are separated by `AND` keyword or comma (`,`),
    Or-rules are separated by `OR` keyword.

    If `exclusion_type` is not specified for a given rule it's treated
    as GOOGLE_ADS_INFO.
    """
    if not raw_rules:
        return None
    rules: list[list[Rule]] = []
    default_exclusion_type = 'GOOGLE_ADS_INFO'
    if isinstance(raw_rules, str):
        raw_rules = _format_raw_rules(raw_rules)
    for rule in raw_rules:
        current_exclusion_type = None
        if ',' in rule:
            types = rule.split(',')
        else:
            types = rule.split(' AND ')
        rule_entry: list[Rule] = []
        for type_ in types:
            spec_ = type_.split(':')
            if len(spec_) == 1:
                if not current_exclusion_type:
                    exclusion_type = default_exclusion_type
                else:
                    exclusion_type = str(current_exclusion_type)
                expression_position = 0
            else:
                exclusion_type = spec_[0]
                expression_position = 1
            current_exclusion_type = str(exclusion_type)
            exclusion_rule = spec_[expression_position].rstrip()
            rule_entry.append(
                Rule(exclusion_type.strip(), exclusion_rule.strip()))
        rules.append(rule_entry)
    return rules


def _format_raw_rules(raw_rules: str) -> list[str]:
    """Applies formatting to raw string rule."""
    # Removes brackets.
    raw_rules = re.sub('[(|)]', '', raw_rules)
    # Adds whitespace in front of operators.
    raw_rules = re.sub('([>|<|>=|<=|!=|=])', r' \1 ', raw_rules)
    # Trims extra whitespace.
    raw_rules = re.sub(' +', ' ', raw_rules)
    # Split rules by OR operator.
    return raw_rules.split(' OR ')
