from tests.fixtures.organization_fixtures import OrganizationInfoFactory, OrganizationSSOFactory
from tests.fixtures.account_fixtures import PaymentInfoFactory
from tests.fixtures.user_fixtures import UserInfoFactory, NotificationRulesFactory
from validations import organization_validator 
import pytest

@pytest.mark.parametrize("field", [
    "organization_name", "address", "city", "state", "zip_code", "country", "sector", "timezone", "language"
])
def test_validate_organization_info(field):
    org_info = OrganizationInfoFactory()
    
    # Checking if valid case passes
    try:
        organization_validator.validate_organization_info(
            org_info["organization_name"],
            org_info["address"],
            org_info["city"],
            org_info["state"],
            org_info["zip_code"],
            org_info["country"],
            org_info["sector"],
            org_info["timezone"],
            org_info["language"]
        )
    except AssertionError:
        pytest.fail(f"Factory-generated valid data failed validation for field '{field}'")

    # Then checking invalid inputs for this field
    for invalid in OrganizationInfoFactory.get_invalid_values(field):
        if field == "zip_code":
            pass
        else:
            org_info[field] = invalid
            with pytest.raises(AssertionError):
                organization_validator.validate_organization_info(
                    org_info["organization_name"],
                    org_info["address"],
                    org_info["city"],
                    org_info["state"],
                    org_info["zip_code"],
                    org_info["country"],
                    org_info["sector"],
                    org_info["timezone"],
                    org_info["language"]
            )

@pytest.mark.parametrize("field", [
    "first_name", "last_name", "iso_code",
    "phone_code", "phone", "user_timezone", "user_language"
])
def test_validate_user_info(field):
    user_info = UserInfoFactory()

    # Checking if factory-generated valid data passes
    try:
        organization_validator.validate_user_info(
            user_info["first_name"],
            user_info["last_name"],
            user_info["iso_code"],
            user_info["phone_code"],
            user_info["phone"],
            user_info["user_timezone"],
            user_info["user_language"]
        )
    except AssertionError:
        pytest.fail(f"Valid factory-generated value for '{field}' failed validation")

    # Now testing with intentionally invalid values
    for invalid in UserInfoFactory.get_invalid_values(field):
        user_info[field] = invalid
        with pytest.raises(AssertionError):
            organization_validator.validate_user_info(
                user_info["first_name"],
                user_info["last_name"],
                user_info["iso_code"],
                user_info["phone_code"],
                user_info["phone"],
                user_info["user_timezone"],
                user_info["user_language"]
            )

@pytest.mark.parametrize("field", [
    "brand", "last_four", "card_token", "handler", "is_default"
])
def test_validate_payment_info_invalid_fields(field):
    payment_info = PaymentInfoFactory()

    # Checking if factory generates valid data
    try:
        organization_validator.validate_payment_info(
            payment_info["brand"],
            payment_info["last_four"],
            payment_info["card_token"],
            payment_info["handler"],
            payment_info["is_default"]
        )
    except AssertionError:
        pytest.fail(f"Valid factory-generated value for '{field}' failed validation")

    # Checking if invalid data raises AssertionError
    for invalid in PaymentInfoFactory.get_invalid_values(field):
        payment_info[field] = invalid
        with pytest.raises(AssertionError):
            organization_validator.validate_payment_info(
                payment_info["brand"],
                payment_info["last_four"],
                payment_info["card_token"],
                payment_info["handler"],
                payment_info["is_default"]
            )
@pytest.mark.parametrize("field", ["minutes_buffer", "notification_method"])
def test_validate_notification_rules_invalid_keys(field):
    """
    Rebuild the notification rules dict with invalid keys or values,
    depending on the parameterized field.
    """
    base_rules = NotificationRulesFactory()
    # print(f"Base rules: {base_rules}")
    for invalid in NotificationRulesFactory.get_invalid_values(field):
        invalid_rules = {}

        if field == "minutes_buffer":
            # Replace a valid int key with an invalid one
            keys = list(base_rules.keys())
            if not keys:
                continue
            valid_key = keys[0]
            invalid_rules[invalid] = base_rules[valid_key]
        elif field == "notification_method":
            # Keep valid key(s) but replace value(s)
            for key in base_rules:
                invalid_rules[key] = invalid
                break

        with pytest.raises(AssertionError):
            organization_validator.validate_notification_rules(invalid_rules)


@pytest.mark.parametrize("invalid_rules", NotificationRulesFactory.get_invalid_values("rules"))
def test_validate_notification_rules_invalid_structures(invalid_rules):
    """
    Directly test structurally invalid 'rules' objects.
    """
    with pytest.raises(AssertionError):
        organization_validator.validate_notification_rules(invalid_rules)


def test_validate_notification_rules_valid():
    rules = NotificationRulesFactory()
    try:
        organization_validator.validate_notification_rules(rules)
    except AssertionError:
        pytest.fail("validate_notification_rules raised AssertionError unexpectedly!")

@pytest.mark.parametrize("field", [
    "organization_id", "integration_type", "direct_login", "auto_provision", "role_id",
    "saml_certificate", "saml_key", "login_url", "logout_url", "metadata_url", "entity_id",
    "vendor_id", "vendor_subdomain", "additional_info"
])
def test_validate_organization_sso_settings_invalid_fields(field):
    sso_info = OrganizationSSOFactory()
    for invalid in OrganizationSSOFactory.get_invalid_values(field):
        sso_info[field] = invalid
        # Forcing integration_type to OKTA when testing additional_info
        if field == "additional_info":
            sso_info["integration_type"] = "OKTA"
        print(f"Testing field '{field}' with invalid value: {invalid}")
        with pytest.raises(AssertionError):
            organization_validator.validate_organization_sso_settings(
                sso_info["organization_id"],
                sso_info["integration_type"],
                sso_info["direct_login"],
                sso_info["auto_provision"],
                sso_info["role_id"],
                sso_info["saml_certificate"],
                sso_info["saml_key"],
                sso_info["login_url"],
                sso_info["logout_url"],
                sso_info["metadata_url"],
                sso_info["entity_id"],
                sso_info["vendor_id"],
                sso_info["vendor_subdomain"],
                sso_info["additional_info"]
            )

def test_validate_organization_sso_settings_valid():
    sso_info = OrganizationSSOFactory()
    try:
        organization_validator.validate_organization_sso_settings(
            sso_info["organization_id"],
            sso_info["integration_type"],
            sso_info["direct_login"],
            sso_info["auto_provision"],
            sso_info["role_id"],
            sso_info["saml_certificate"],
            sso_info["saml_key"],
            sso_info["login_url"],
            sso_info["logout_url"],
            sso_info["metadata_url"],
            sso_info["entity_id"],
            sso_info["vendor_id"],
            sso_info["vendor_subdomain"],
            sso_info["additional_info"]
        )
    except AssertionError:
        pytest.fail("validate_organization_sso_settings raised AssertionError unexpectedly!")
def test_validate_organization_sso_settings_empty_fields():
    sso_info = OrganizationSSOFactory()
    sso_info["organization_id"] = None
    sso_info["integration_type"] = None
    sso_info["direct_login"] = None
    sso_info["auto_provision"] = None
    sso_info["role_id"] = None
    sso_info["saml_certificate"] = None
    sso_info["saml_key"] = None
    sso_info["login_url"] = None
    sso_info["logout_url"] = None
    sso_info["metadata_url"] = None
    sso_info["entity_id"] = None
    sso_info["vendor_id"] = None
    sso_info["vendor_subdomain"] = None
    sso_info["additional_info"] = None

    with pytest.raises(AssertionError):
        organization_validator.validate_organization_sso_settings(
            sso_info["organization_id"],
            sso_info["integration_type"],
            sso_info["direct_login"],
            sso_info["auto_provision"],
            sso_info["role_id"],
            sso_info["saml_certificate"],
            sso_info["saml_key"],
            sso_info["login_url"],
            sso_info["logout_url"],
            sso_info["metadata_url"],
            sso_info["entity_id"],
            sso_info["vendor_id"],
            sso_info["vendor_subdomain"],
            sso_info["additional_info"]
        )
def test_validate_organization_sso_settings_invalid_additional_info():
    sso_info = OrganizationSSOFactory()
    sso_info["additional_info"] = "invalid_string"  # Should be a dict
    sso_info["integration_type"] = "OKTA"  # Forcing integration_type to OKTA for additional_info validation
    with pytest.raises(AssertionError):
        organization_validator.validate_organization_sso_settings(
            sso_info["organization_id"],
            sso_info["integration_type"],
            sso_info["direct_login"],
            sso_info["auto_provision"],
            sso_info["role_id"],
            sso_info["saml_certificate"],
            sso_info["saml_key"],
            sso_info["login_url"],
            sso_info["logout_url"],
            sso_info["metadata_url"],
            sso_info["entity_id"],
            sso_info["vendor_id"],
            sso_info["vendor_subdomain"],
            sso_info["additional_info"]
        )
def test_validate_organization_sso_settings_missing_required_fields():
    sso_info = OrganizationSSOFactory()
    sso_info["organization_id"] = None  # Missing required field

    with pytest.raises(AssertionError):
        organization_validator.validate_organization_sso_settings(
            sso_info["organization_id"],
            sso_info["integration_type"],
            sso_info["direct_login"],
            sso_info["auto_provision"],
            sso_info["role_id"],
            sso_info["saml_certificate"],
            sso_info["saml_key"],
            sso_info["login_url"],
            sso_info["logout_url"],
            sso_info["metadata_url"],
            sso_info["entity_id"],
            sso_info["vendor_id"],
            sso_info["vendor_subdomain"],
            sso_info["additional_info"]
        )

@pytest.mark.parametrize("field", [
    "organization_id", "integration_type", "direct_login", "auto_provision", "role_id",
    "saml_certificate", "saml_key", "login_url", "logout_url", "metadata_url", "entity_id",
    "vendor_id", "vendor_subdomain", "additional_info"
])
def test_validate_organization_sso_settings_invalid_field_types(field):
    sso_info = OrganizationSSOFactory()
    for invalid in OrganizationSSOFactory.get_invalid_values(field):
        sso_info[field] = invalid
        # Forcing integration_type to OKTA when testing additional_info
        if field == "additional_info":
            sso_info["integration_type"] = "OKTA"
        print(f"Testing field '{field}' with invalid value: {invalid}")
        with pytest.raises(AssertionError):
            organization_validator.validate_organization_sso_settings(
                sso_info["organization_id"],
                sso_info["integration_type"],
                sso_info["direct_login"],
                sso_info["auto_provision"],
                sso_info["role_id"],
                sso_info["saml_certificate"],
                sso_info["saml_key"],
                sso_info["login_url"],
                sso_info["logout_url"],
                sso_info["metadata_url"],
                sso_info["entity_id"],
                sso_info["vendor_id"],
                sso_info["vendor_subdomain"],
                sso_info["additional_info"]
            )

