#
# # Copyright © 2024 Peak AI Limited. or its affiliates. All Rights Reserved.
# #
# # Licensed under the Apache License, Version 2.0 (the "License"). You
# # may not use this file except in compliance with the License. A copy of
# # the License is located at:
# #
# # https://github.com/PeakBI/peak-sdk/blob/main/LICENSE
# #
# # or in the "license" file accompanying this file. This file 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.
# #
# # This file is part of the peak-sdk.
# # see (https://github.com/PeakBI/peak-sdk)
# #
# # You should have received a copy of the APACHE LICENSE, VERSION 2.0
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
#
"""Generic utilities."""

from __future__ import annotations

import re
from typing import Any, MutableMapping

# Masking patterns
BANK_CARD_PATTERN: re.Pattern[str] = re.compile(r"\b(?:\d[- ]?){13,16}\b")
EMAIL_PATTERN: re.Pattern[str] = re.compile(r"[\w+\.+\-]+@+[\w+\.+\-]+[\.\w]{2,}")
PHONE_NUM_PATTERN: re.Pattern[str] = re.compile(
    r"\+\d{1,3}\s?(\(\d{1,4}\)|\d{1,4})[\s.-]?\d{1,4}[\s.-]?\d{1,9}",
)
JWT_PATTERN: re.Pattern[str] = re.compile(r"\bey[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*\b")
SSN_PATTERN: re.Pattern[str] = re.compile(r"\b(\d{4}[-\s]?\d{4}[-\s]?\d{4}|\d{3}-\d{2}-\d{4})\b")


# Masking configs
MASK_SYMBOL = "*"
MAX_CHARS_TO_MASK = 32
VISIBLE_CHARS_FROM_START = 3
VISIBLE_CHARS_FROM_END = 3

PII_PATTERNS: list[re.Pattern[str]] = [
    BANK_CARD_PATTERN,
    EMAIL_PATTERN,
    PHONE_NUM_PATTERN,
    JWT_PATTERN,
    SSN_PATTERN,
]


def mask_pii_data(_str: str) -> str:
    """Masks sensitive PII data in the input string.

    Args:
        _str (str): The input string containing sensitive information.

    Returns:
        str: The same input string with sensitive information masked.
    """

    def mask_pattern(match: re.Match[str]) -> str:
        match_text: str = match.group()
        mask_length: int = min(len(match_text), MAX_CHARS_TO_MASK)
        visible_start: str = match_text[:VISIBLE_CHARS_FROM_START]
        visible_end: str = match_text[-VISIBLE_CHARS_FROM_END:]
        masked_chars: str = MASK_SYMBOL * mask_length
        return visible_start + masked_chars + visible_end

    masked_string: str = _str
    for regex_pattern in PII_PATTERNS:
        masked_string = regex_pattern.sub(mask_pattern, masked_string)

    return masked_string


def mask_nested_pii_data(_dict: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
    """Masks sensitive PII data in the input dictionary.

    Args:
        _dict (MutableMapping): The input dictionary containing sensitive information.

    Returns:
        MutableMapping: The same input dictionary with sensitive information masked.
    """

    def mask_value(value: Any) -> Any:
        if isinstance(value, str):
            return mask_pii_data(value)
        return value

    for key, value in _dict.items():
        if isinstance(value, MutableMapping):
            # Recursively call mask_nested_pii_data on nested mappings
            _dict[key] = mask_nested_pii_data(value)
        else:
            _dict[key] = mask_value(value)

    return _dict
