import copy
import json
import uuid

from cryptojwt import JWS
from cryptojwt.jwk.ec import new_ec_key
from satosa.context import Context

from pyeudiw.satosa.backends.openid4vp.schemas.flow import RemoteFlowType
from pyeudiw.satosa.utils.validation import OAUTH_CLIENT_ATTESTATION_POP_HEADER, OAUTH_CLIENT_ATTESTATION_HEADER
from pyeudiw.tools.content_type import HTTP_CONTENT_TYPE_HEADER, FORM_URLENCODED

BASE_PACKAGE = "pyeudiw.satosa.frontends.openid4vci"
_JWS_HELPER_MODULE = "pyeudiw.jwt.jws_helper.JWSHelper"
JWS_HELPER_VERIFY_MODULE = f"{_JWS_HELPER_MODULE}.verify"

MOCK_METADATA_JWKS_CONFIG = [
    {
        "kty": "EC",
        "d": "i0HQiqDPXf-MqC776ztbgOCI9-eARhcUczqJ-7_httc",
        "use": "sig",
        "crv": "P-256",
        "kid": "f10aca0992694b3581f6f699bfc8a2c6cc687725",
        "x": "jE2RpcQbFQxKpMqehahgZv6smmXD0i/LTP2QRzMADk4",
        "y": "qkMx5iqt5PhPu5tfctS6HsP+FmLgrxfrzUV2GwMQuh8",
        "alg": "ES256",
    }
]

MOCK_TRUST_CONFIG = {
    "federation": {
        "config": {
            "entity_configuration_exp": 600,
            "default_sig_alg": "ES256",
            "federation_jwks": MOCK_METADATA_JWKS_CONFIG,
            "authority_hints": [],
        }
    }
}

MOCK_USER_STORAGE_CONFIG = {
    "mongo_db": {
        "storage": {
            "module": "pyeudiw.storage.user_storage",
            "class": "UserStorage",
            "init_params": {
                "url": "mongodb://satosa-mongo:27017",
                "conf": {"db_name": "pyeudiw_test", "db_users_collection": "users", "data_ttl": 63072000},
                "connection_params": {"username": "user", "password": "psw"},
            },
        }
    }
}

MOCK_CREDENTIAL_STORAGE_CONFIG = {
    "mongo_db": {
        "storage": {
            "module": "pyeudiw.storage.credential_storage",
            "class": "CredentialStorage",
            "init_params": {
                "url": "mongodb://satosa-mongo:27017",
                "conf": {"db_name": "pyeudiw_test", "db_users_collection": "credentials", "data_ttl": 63072000},
                "connection_params": {"username": "user", "password": "psw"},
            },
        }
    }
}

MOCK_ENDPOINTS_CONFIG = {
    "par": {"module": f"{BASE_PACKAGE}.endpoints.pushed_authorization_request_endpoint", "class": "ParHandler", "path": "/par"},
    "credential_offer": {"module": f"{BASE_PACKAGE}.endpoints.credential_offer_endpoint", "class": "CredentialOfferHandler", "path": "/credential"},
    "authorization_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.authorization_endpoint", "class": "AuthorizationHandler", "path": "/authorization"},
    "token_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.token_endpoint", "class": "TokenHandler", "path": "/token"},
    "nonce_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.nonce_endpoint", "class": "NonceHandler", "path": "/nonce-endpoint"},
    "credential_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.credential_endpoint", "class": "CredentialHandler", "path": "/credential"},
    "deferred_credential_endpoint": {
        "module": f"{BASE_PACKAGE}.endpoints.deferred_credential_endpoint",
        "class": "DeferredCredentialHandler",
        "path": "/deferred-credential",
    },
    "notification_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.notification_endpoint", "class": "NotificationHandler", "path": "/notification"},
    "metadata_endpoint": {"module": f"{BASE_PACKAGE}.endpoints.metadata_endpoint", "class": "MetadataHandler", "path": "/.well-known/openid-federation"},
}

MOCK_STATUS_LIST_CONFIG = {
    "path": "/status",
    "exp": 90,
    "ttl": 43200,
}
MOCK_CREDENTIAL_CONFIGURATIONS = {
    "lookup_source": "openid4vci",
    "status_list": MOCK_STATUS_LIST_CONFIG,
    "credential_specification": {
        "dc_sd_jwt_mDL": {
            "template": """
                holder_disclosed_claims:
                    !sd given_name: "{{name}}"
                    !sd family_name: "{{surname}}"
                    !sd place_of_birth:
                        country: "{{countyOfBirth}}"
                        locality: "{{placeOfBirth}}"
                key_binding: true
                user_claims:
                    !sd birthdate: "{{dateOfBirth}}"
                    !sd family_name: "{{surname}}"
                    !sd given_name: "{{name}}"
                    !sd place_of_birth:
                        country: "{{countyOfBirth}}"
                        locality: "{{placeOfBirth}}"
                    !sd tax_id_code: "TINIT-{{fiscal_code}}"
                    !sd unique_id: "{{unique_id}}"
            """
        },
        "mso_mdoc_mDL": {
            "template": """
                holder_disclosed_claims:
                    !sd given_name: "{{name}}"
                    !sd family_name: "{{surname}}"
                    !sd place_of_birth:
                        country: "{{countyOfBirth}}"
                        locality: "{{placeOfBirth}}"
                key_binding: true
                user_claims:
                    !sd birthdate: "{{dateOfBirth}}"
                    !sd family_name: "{{surname}}"
                    !sd given_name: "{{name}}"
                    !sd place_of_birth:
                        country: "{{countyOfBirth}}"
                        locality: "{{placeOfBirth}}"
                    !sd tax_id_code: "TINIT-{{fiscal_code}}"
                    !sd unique_id: "{{unique_id}}"
            """
        },
    },
}

MOCK_OPENID_CREDENTIAL_ISSUER_CONFIG = {
    "credential_configurations_supported": {
        "dc_sd_jwt_EuropeanDisabilityCard": {"format": "dc+sd-jwt", "scope": "EuropeanDisabilityCard"},
        "dc_sd_jwt_mDL": {"format": "dc+sd-jwt", "scope": "mDL", "cryptographic_binding_methods_supported": ["jwk"]},
        "mso_mdoc_mDL": {"doctype": "org.iso.18013.5.1.mDL", "format": "mso_mdoc", "scope": "mDL", "cryptographic_binding_methods_supported": ["cose_key"]},
    },
    "authorization_servers": [],
    "credential_issuer": "",
}

MOCK_OAUTH_AUTHORIZATION_SERVER_CONFIG = {
    "response_types_supported": ["code"],
    "response_modes_supported": ["form_post.jwt", "query"],
    "code_challenge_methods_supported": ["S256"],
    "scopes_supported": ["scope1", "scope2", "openid"],
}

MOCK_JWT_CONFIG = {
    "default_sig_alg": "ES256",
    "default_enc_alg": "RSA-OAEP",
    "default_enc_enc": "A256CBC-HS512",
    "default_exp": 6,
    "enc_alg_supported": ["RSA-OAEP", "RSA-OAEP-256", "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"],
    "enc_enc_supported": ["A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", "A128GCM", "A192GCM", "A256GCM"],
    "sig_alg_supported": ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512"],
    "access_token_exp": 90,
    "refresh_token_exp": 120,
    "par_exp": 90,
}

MOCK_STORAGE_CONFIG = {
    "storage": {
        "mongo_db": {
            "cache": {
                "module": "pyeudiw.storage.mongo_cache",
                "class": "MongoCache",
                "init_params": {
                    "url": "mongodb://satosa-mongo:27017",
                    "conf": {"db_name": "pyeudiw_test"},
                    "connection_params": {"username": "user", "password": "psw"},
                },
            },
            "storage": {
                "module": "pyeudiw.storage.mongo_storage",
                "class": "MongoStorage",
                "init_params": {
                    "url": "mongodb://satosa-mongo:27017",
                    "conf": {
                        "db_name": "pyeudiw_test",
                        "db_sessions_collection": "sessions",
                        "db_trust_attestations_collection": "trust_attestations",
                        "db_trust_anchors_collection": "trust_anchors",
                        "db_trust_sources_collection": "trust_sources",
                        "data_ttl": 63072000,
                    },
                    "connection_params": {"username": "user", "password": "psw"},
                },
            },
        }
    }
}

MOCK_QR_CODE_CONFIG = {
    "size": 250,
    "color": "#000000",
    "expiration_time": 120,
    "logo_path": "wallet-it/wallet-icon-blue.svg",
    "ui": {
        "static_storage_url": "http://localhost:static",
        "template_folder": "templates",
        "qrcode_template": "qr_code.html",
        "authorization_error_template": "authorization_error.html",
    },
}

MOCK_PYEUDIW_FRONTEND_CONFIG = {
    "endpoints": MOCK_ENDPOINTS_CONFIG,
    "qrcode": MOCK_QR_CODE_CONFIG,
    "jwt": MOCK_JWT_CONFIG,
    "metadata": {"oauth_authorization_server": MOCK_OAUTH_AUTHORIZATION_SERVER_CONFIG, "openid_credential_issuer": MOCK_OPENID_CREDENTIAL_ISSUER_CONFIG},
    "user_storage": MOCK_USER_STORAGE_CONFIG,
    "credential_storage": MOCK_CREDENTIAL_STORAGE_CONFIG,
    "metadata_jwks": MOCK_METADATA_JWKS_CONFIG,
    "credential_configurations": MOCK_CREDENTIAL_CONFIGURATIONS,
    "trust": MOCK_TRUST_CONFIG,
    "storage": MOCK_STORAGE_CONFIG,
    "security": {
        "dpop_required": False,
    },
}

MOCK_INTERNAL_ATTRIBUTES = {
    "attributes": {
        "mail": {"openid4vci": ["mail", "email"]},
        "name": {"openid4vci": ["given_name"]},
        "surname": {"openid4vci": ["family_name"]},
        "placeOfBirth": {"openid4vci": ["placeOfBirth"]},
        "countyOfBirth": {"openid4vci": ["countyOfBirth"]},
        "dateOfBirth": {"openid4vci": ["dateOfBirth"]},
        "fiscal_code": {"openid4vci": ["fiscal_code", "fiscal_number", "personal_administrative_number"]},
    }
}

MOCK_BASE_URL = "example.com"
MOCK_NAME = "openid4vcimock"

INVALID_METHOD_FOR_POST_REQ = ["GET", "PUT", "DELETE", "PATCH"]

INVALID_METHOD_FOR_GET_REQ = ["POST", "PUT", "DELETE", "PATCH"]

INVALID_CONTENT_TYPES_NOT_FORM_URLENCODED = [
    "application/json",
    "application/xml",
    "application/pdf",
    "application/zip",
    "application/octet-stream",
    "application/x-pem-file",
    "application/jwt",
    "application/pkcs10",
    "application/pkcs7-mime",
    "application/ld+json",
    "application/vnd.api+json",
    "text/plain",
    "text/html",
    "text/css",
    "text/csv",
    "text/xml",
    "image/png",
    "image/jpeg",
    "image/svg+xml",
    "audio/mpeg",
    "video/mp4",
]

INVALID_CONTENT_TYPES_NOT_APPLICATION_JSON = [
    "text/plain",
    "text/html",
    "text/css",
    "text/csv",
    "text/xml",
    "application/xml",
    "application/x-www-form-urlencoded",
    "multipart/form-data",
    "application/pdf",
    "application/zip",
    "application/octet-stream",
    "image/png",
    "image/jpeg",
    "image/svg+xml",
    "audio/mpeg",
    "video/mp4",
    "application/x-pem-file",
    "application/jwt",
    "application/pkcs10",
    "application/pkcs7-mime",
    "application/ld+json",
    "application/vnd.api+json",
]

INVALID_ATTESTATION_HEADERS = [
    {OAUTH_CLIENT_ATTESTATION_HEADER: "", OAUTH_CLIENT_ATTESTATION_POP_HEADER: "valid"},
    {OAUTH_CLIENT_ATTESTATION_HEADER: None, OAUTH_CLIENT_ATTESTATION_POP_HEADER: "valid"},
    {OAUTH_CLIENT_ATTESTATION_POP_HEADER: "valid"},
    {OAUTH_CLIENT_ATTESTATION_HEADER: "valid", OAUTH_CLIENT_ATTESTATION_POP_HEADER: ""},
    {OAUTH_CLIENT_ATTESTATION_HEADER: "valid", OAUTH_CLIENT_ATTESTATION_POP_HEADER: None},
    {OAUTH_CLIENT_ATTESTATION_HEADER: "valid"},
    {OAUTH_CLIENT_ATTESTATION_HEADER: "", OAUTH_CLIENT_ATTESTATION_POP_HEADER: ""},
    {OAUTH_CLIENT_ATTESTATION_HEADER: None, OAUTH_CLIENT_ATTESTATION_POP_HEADER: None},
    {},
]


def mock_valid_oauth_client_attestation_jwt(crv="P-256", use="sig", kid="ec1", alg="ES256"):
    ec_key = new_ec_key(crv=crv, use=use, kid=kid)
    payload = {"cnf": ec_key.serialize(private=False)}
    jws = JWS(json.dumps(payload), alg=alg)
    return jws.sign_compact([ec_key])


def get_mocked_openid4vpi_entity() -> dict:
    return {
        "document_id": str(uuid.uuid4()),
        "request_uri_part": "request_uri_part",
        "state": "xyz456",
        "session_id": "sessionid",
        "remote_flow_typ": RemoteFlowType.SAME_DEVICE,
        "client_id": "client123",
        "code_challenge": "ef7a1e840dad06e97982b64f8575064303408f187af733444bc6eed9b543d043",  # as sha256("code_verifier".encode('utf-8')).hexdigest()
        "code_challenge_method": "S256",
        "redirect_uri": "https://client.com",
        "authorization_details": [],
    }


def get_mocked_satosa_context(
    method="POST", content_type=FORM_URLENCODED, headers=None, oauth_client_attestation_header=mock_valid_oauth_client_attestation_jwt()
) -> Context:
    if headers is None:
        headers = {
            HTTP_CONTENT_TYPE_HEADER: content_type,
            OAUTH_CLIENT_ATTESTATION_POP_HEADER: "valid-pop",
            OAUTH_CLIENT_ATTESTATION_HEADER: oauth_client_attestation_header,
            "HTTP_USER_AGENT": "Mozilla/5.0 (Linux; Android 10; SM-G960F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36",
        }
    elif HTTP_CONTENT_TYPE_HEADER not in headers:
        headers[HTTP_CONTENT_TYPE_HEADER] = content_type
    context = Context()
    context.request_method = method
    context.http_headers = headers
    context.state = {"SESSION_ID": "sessionid"}
    return context


def get_pyeudiw_frontend_config_with_openid_credential_issuer(openid_credential_issuer=None):
    if openid_credential_issuer:
        config = copy.deepcopy(MOCK_PYEUDIW_FRONTEND_CONFIG)
        metadata = config.get("metadata", {})
        openid_issuer = metadata.get("openid_credential_issuer", {})
        openid_issuer["credential_issuer"] = openid_credential_issuer
        return config

    return MOCK_PYEUDIW_FRONTEND_CONFIG


REMOVE = object()  # special value to remove keys


def mock_deserialized_overridable(base: dict, overrides=None):
    import copy

    def set_or_remove(d: dict, path: list[str], value):
        *parents, last = path
        current = d
        for p in parents:
            current = current.setdefault(p, {})  # crea i dict intermedi se mancanti

        if value is REMOVE:
            current.pop(last, None)
        else:
            current[last] = value

    result = copy.deepcopy(base)

    if overrides:
        for k, v in overrides.items():
            path = k.split(".")
            set_or_remove(result, path, v)

    return result
