"""
Factory definitions for unit test data generation using Factory Boy and Faker.

These factories generate plain dictionaries (not Django models) to simulate input data
for validators such as service, team, integration configurations, organization setup,
user info, SSO, and more.

Author: Md. Fahim Bin Amin & Asif Al Shahariar
"""


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


# Faker instance to generate realistic fake data
fake = Faker()

# Central map of fields to types or semantic categories
FIELD_TYPE_MAP = {
    # Organization Info (all strings)
    "organization_name": "str",
    "address": "str",
    "city": "str",
    "state": "str",
    "zip_code": "str",
    "country": "str",
    "sector": "str",
    "timezone": "str",
    "language": "str",

    # Organization SSO Info
    "organization_id": "int",
    "integration_type": "str",
    "direct_login": "bool",
    "auto_provision": "bool",
    "role_id": "str",
    "saml_certificate": "str",
    "saml_key": "str",
    "login_url": "str",
    "logout_url": "str",
    "metadata_url": "str",
    "entity_id": "str",
    "vendor_id": "int",
    "vendor_subdomain": "str",
    "additional_info": "dict"
}



# ===================== Organization Factories =====================

class OrganizationInfoFactory(factory.Factory):
    """
    Factory to generate mock organization details.
    """
    class Meta:
        model = dict

    organization_name = factory.LazyAttribute(lambda _: fake.company())
    address = factory.LazyAttribute(lambda _: fake.address())
    city = factory.LazyAttribute(lambda _: fake.city())
    state = factory.LazyAttribute(lambda _: fake.state())
    zip_code = factory.LazyAttribute(lambda _: fake.postcode())
    country = factory.LazyAttribute(lambda _: fake.random_element(configs.constants.all_country_codes.keys()))
    sector = factory.LazyAttribute(lambda _: fake.random_element(configs.allowed_sectors))
    timezone = factory.LazyAttribute(lambda _: fake.random_element(pytz.all_timezones))
    language = factory.LazyAttribute(lambda _: fake.random_element(configs.allowed_languages))

    @staticmethod
    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 ["organization_name","city", "state"]:
            return [
                "",
            ]
        elif field_name in ["address"]:
            return [
                "123 Main St; drop table users;",  # SQL injection attempt
                "456 Elm St, insert into users (name) values ('test');",  # SQL injection attempt
                "789 Oak St, delete from users where id=1;",  # SQL injection attempt
            ]

        elif field_name in [ "country", "sector", "timezone", "language"]:
            return [
                "",  # Empty string
                "@@!!##",  # Special characters
                fake.text(max_nb_chars=300),  # Long random text
            ]
        
        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
 


class OrganizationSSOFactory(factory.Factory):
    """
    Factory to generate mock SSO configuration for an organization.
    """
    class Meta:
        model = dict

    organization_id = factory.LazyAttribute(lambda _: fake.random_int(min=1, max=1000))
    integration_type = factory.LazyAttribute(lambda _: fake.random_element(elements=configs.allowed_sso_types))
    direct_login = factory.LazyAttribute(lambda _: fake.boolean())
    auto_provision = factory.LazyAttribute(lambda _: fake.boolean())
    role_id = factory.LazyAttribute(lambda _: fake.random_element(list(roles.user_role_maps.keys())))
    saml_certificate = factory.LazyAttribute(lambda _: fake.sentence())
    saml_key = factory.LazyAttribute(lambda _: fake.sentence())
    login_url = factory.LazyAttribute(lambda _: fake.url())
    logout_url = factory.LazyAttribute(lambda _: fake.url())
    metadata_url = factory.LazyAttribute(lambda _: fake.url())
    entity_id = factory.LazyAttribute(lambda _: fake.uuid4())
    vendor_id = factory.LazyAttribute(lambda _: str(fake.random_int(min=1, max=1000)))
    vendor_subdomain = factory.LazyAttribute(lambda _: fake.domain_name())
    additional_info = factory.LazyAttribute(
        lambda _: {
            var_names.client_id: str(fake.uuid4()),
            var_names.client_secret: str(fake.uuid4())
        }
    )

    @staticmethod
    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 == "organization_id":
            return [
                "not_an_int",  # String
                None,  # NoneType
                3.14,  # Float
            ]
        elif field_name in ["saml_certificate", "saml_key", "entity_id","vendor_subdomain","vendor_id"]:
            return [
                12345,  # Integer
                ["list", "of", "strings"],  # List
                {"key": "value"},  # Dict
            ]
        
        elif field_name == "additional_info":
            return [
                # None,
                {},
                {"key": "value"},
                {var_names.client_id: None},
                {var_names.client_secret: None},
                {var_names.client_id: "", var_names.client_secret: ""},
                {var_names.client_id: "abc"},  # Missing client_secret
                {var_names.client_secret: "xyz"},  # Missing client_id
                {var_names.client_id: "abc", "wrong_key": "xyz"},  # Invalid key set
            ]
        
        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
