# Copyright 2026 Philterd, 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.

from __future__ import annotations

import re
from typing import List

from phileas.models.span import Span
from .base import BaseFilter, FilterType


_PATTERNS = [
    # Visa
    re.compile(r"\b4[0-9]{12}(?:[0-9]{3})?\b"),
    # MasterCard
    re.compile(r"\b(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}\b"),
    # American Express
    re.compile(r"\b3[47][0-9]{13}\b"),
    # Discover
    re.compile(r"\b6(?:011|5[0-9]{2})[0-9]{12}\b"),
    # Diners Club
    re.compile(r"\b3(?:0[0-5]|[68][0-9])[0-9]{11}\b"),
    # JCB
    re.compile(r"\b(?:2131|1800|35\d{3})\d{11}\b"),
]


def _luhn_check(number: str) -> bool:
    """Return True if *number* (digits only) passes the Luhn algorithm."""
    digits = [int(d) for d in number]
    odd_digits = digits[-1::-2]
    even_digits = digits[-2::-2]
    total = sum(odd_digits)
    for d in even_digits:
        total += sum(divmod(d * 2, 10))
    return total % 10 == 0


class CreditCardFilter(BaseFilter):
    def __init__(self, filter_config):
        super().__init__(FilterType.CREDIT_CARD, filter_config)

    def filter(self, text: str, context: str = "default") -> List[Span]:
        luhn_check_enabled = getattr(self.filter_config, "luhn_check", False)
        if not luhn_check_enabled:
            return self._find_spans(_PATTERNS, text, context)

        # When Luhn check is enabled, only include spans that pass validation
        spans = self._find_spans(_PATTERNS, text, context)
        return [s for s in spans if _luhn_check("".join(c for c in s.text if c.isdigit()))]
