from faker import Faker
from utils import constants, roles, var_names
import configuration as configs
import factory
import pytz
import random

fake = Faker()

# Central map of fields to types or semantic categories
FIELD_TYPE_MAP = {
    # User Info
    "first_name": "str",
    "last_name": "str",
    "iso_code": "str",
    "phone_code": "str",
    "phone": "str",
    "user_timezone": "str",
    "user_language": "str",

    # Notification Info
    "rules": "dict",  # Complex type
    "minutes_buffer": "int",
    "notification_method": "list",

}

class UserInfoFactory(factory.Factory):
    """
    Factory to generate mock user information.
    """
    class Meta:
        model = dict

    first_name = factory.LazyAttribute(lambda _: fake.first_name())
    last_name = factory.LazyAttribute(lambda _: fake.last_name())
    iso_code = factory.LazyAttribute(lambda _: fake.random_element(constants.all_country_codes.keys()))
    phone_code = factory.LazyAttribute(lambda _: str(fake.random_int(min=1, max=999)))
    phone = factory.LazyAttribute(lambda _: fake.msisdn())
    user_timezone = factory.LazyAttribute(lambda _: fake.random_element(pytz.all_timezones))
    user_language = factory.LazyAttribute(lambda _: fake.random_element(configs.allowed_languages))


    def get_invalid_values(field_name):
        """
        Returns a list of invalid but type-consistent or semantically incorrect
        values for a given field. Prevents type errors but supports validation testing.
        """
        expected_type = FIELD_TYPE_MAP.get(field_name)

        if field_name in ["first_name", "last_name"]:
            return [
                "",
            ]

        elif field_name in ["iso_code"]:
            return [
                "",  # Empty string
                "@@!!##",  # Special characters
                fake.text(max_nb_chars=300),  # Long random text
            ]
        elif field_name == "phone_code":
            return [
            "",             # Empty string
            "abcd",         # Alphabetic
            "+123",         # Plus sign included
            "12a",          # Alphanumeric
            "1234",         # Too long 
            " ",            # Whitespace
            ]

        elif field_name == "phone":
            return [
                "",  # Empty string
                "@@!!##",  # Special characters
                fake.text(max_nb_chars=300),  # Long random text
                "12345abcde",  # Alphanumeric string
            ]

        elif field_name == "user_timezone":
            return [
                "",  # Empty string
                "@@!!##",  # Special characters
                fake.text(max_nb_chars=300),  # Long random text
            ]

        elif field_name == "user_language":
            return [
                "",  # Empty string
                "@@!!##",  # Special characters
                fake.text(max_nb_chars=300),  # Long random text
            ]
        elif expected_type == "int":
            return [-999, 0, 999999999999999]
        elif expected_type == "bool":
            return ["yes", "no", "truthy", 1, 0]
        elif expected_type == "list":
            return [[], ["", "", ""]]
        elif expected_type == "dict":
            return [{}, {"key": ""}]
        else:
            return [None]  # fallback


class NotificationRulesFactory(factory.Factory):
    """
    Factory to generate mock notification rules.
    Returns a dict like: { minutes_buffer (int): [notification_method, ...] }
    """
    class Meta:
        model = dict

    @classmethod
    def _create(cls, model_class, *args, **kwargs):
        return {
            fake.random_int(min=0, max=120):
            fake.random_elements(
                elements=configs.allowed_notification_methods,
                unique=True,
                length=fake.random_int(min=1, max=3)
            )
            for _ in range(3)
        }

    def get_invalid_values(field_name):
        """
        Returns a list of invalid but type-consistent or semantically incorrect
        values for a given field. Prevents type errors but supports validation testing.
        """
        expected_type = FIELD_TYPE_MAP.get(field_name)

        if field_name == "rules":
            return [
                None,
                {},
                [],
                {"not_int_key": ["email"]},
                {10: ["invalid_method"]},
                {10: []},
                {None: ["email"]},
                {"10": ["email"]},  # string key
                {10: ["email", "sms"]},
                {10: [None]},
                {10: [""]},
            ]

        elif field_name == "notification_method":
            return [
                ["invalid_method"],
                ["", None],
                ["sms", "pigeon"],  # Assuming "pigeon" isn't a valid method
            ]

        elif field_name == "minutes_buffer":
            return [
                "10",  # str
                None,
                1.5,
                -5,  # Negative value

            ]
        
        elif expected_type == "str":    
            return [
                "",
                "   ",
                fake.text(max_nb_chars=300),
                "@@!!##",
            ]
        

        elif expected_type == "int":
            return [-999, 0, 999999999999999]
        elif expected_type == "bool":
            return ["yes", "no", "truthy", 1, 0]
        elif expected_type == "list":
            return [[], ["", "", ""]]
        elif expected_type == "dict":
            return [{}, {"key": ""}]
        else:
            return [None]  # fallback