import pytest
import ecdsa
from bluetooth_mesh.provisioning import (
    ProvisioningAlgorithm,
    ProvisioningPublicKeyType,
    ProvisioningStaticOOBType,
    ProvisioningOutputOOBAction,
    ProvisioningInputOOBAction,
    ProvisioningAuthenticationMethod,
    ProvisioningPDU,
    ProvisioningPDUType,
    provisioning_confirmation,

)

valid = [
    pytest.param(
        bytes.fromhex('0010'),
        dict(
            type=ProvisioningPDUType.INVITE,
            parameters=dict(
                attention=16
            )
        ),
        id="invite"
    ),
    pytest.param(
        bytes.fromhex('010100010101080009050002'),
        dict(
            type=ProvisioningPDUType.CAPABILITIES,
            parameters=dict(
                num_elements=1,
                algorithms={ProvisioningAlgorithm.FIPS_P256_EC},
                public_key_type={ProvisioningPublicKeyType.OOB},
                static_oob_type={ProvisioningStaticOOBType.AVAILABLE},
                output_oob_size=8,
                output_oob_action={ProvisioningOutputOOBAction.BLINK,
                                   ProvisioningOutputOOBAction.OUTPUT_NUMERIC},
                input_oob_size=5,
                input_oob_action={ProvisioningInputOOBAction.TWIST},
            )
        ),
        id="capabilities",
    ),
    pytest.param(
        bytes.fromhex('020000000000'),
        dict(
            type=ProvisioningPDUType.START,
            parameters=dict(
                algorithm=ProvisioningAlgorithm.FIPS_P256_EC,
                public_key=False,
                authentication_method=ProvisioningAuthenticationMethod.NONE,
                authentication_action=0,
                authentication_size=0,
            )
        ),
        id="start, no authentication",
    ),
    pytest.param(
        bytes.fromhex('020001020304'),
        dict(
            type=ProvisioningPDUType.START,
            parameters=dict(
                algorithm=ProvisioningAlgorithm.FIPS_P256_EC,
                public_key=True,
                authentication_method=ProvisioningAuthenticationMethod.OUTPUT,
                authentication_action=ProvisioningOutputOOBAction.OUTPUT_NUMERIC,
                authentication_size=4,
            )
        ),
        id="start, output numeric",
    ),
    pytest.param(
        bytes.fromhex('05b38a114dfdca1fe153bd2c1e0dc46ac2'),
        dict(
            type=ProvisioningPDUType.CONFIRMATION,
            parameters=dict(
                confirmation=bytes.fromhex('b38a114dfdca1fe153bd2c1e0dc46ac2'),
            )
        ),
        id="confirmation",
    ),
    pytest.param(
        bytes.fromhex('068b19ac31d58b124c946209b5db1021b9'),
        dict(
            type=ProvisioningPDUType.RANDOM,
            parameters=dict(
                random=bytes.fromhex('8b19ac31d58b124c946209b5db1021b9'),
            )
        ),
        id="random",
    ),
    pytest.param(
        bytes.fromhex('07'
                      'd0bd7f4a89a2ff6222af59a90a60ad58acfe3123356f5cec29'
                      '73e0ec50783b10c7'),
        dict(
            type=ProvisioningPDUType.DATA,
            parameters=dict(
                encrypted_provisioning_data=bytes.fromhex('d0bd7f4a89a2ff6222af59a90a60ad58acfe3123356f5cec29'),
                provisioning_data_mic=bytes.fromhex('73e0ec50783b10c7'),
            )
        ),
        id="data",
    ),
]


@pytest.mark.parametrize("encoded,decoded", valid)
def test_build(encoded, decoded):
    result = ProvisioningPDU.build(obj=decoded)
    # print(result)
    assert result == encoded


@pytest.mark.parametrize("encoded,decoded", valid)
def test_parse(encoded, decoded):
    result = ProvisioningPDU.parse(data=encoded)
    # print(result)
    assert result == decoded


# NOTE: ecdsa.VerifyingKey does not overload __eq__
@pytest.mark.parametrize("encoded,decoded", [
    pytest.param(
        bytes.fromhex('03'
                      '2c31a47b5779809ef44cb5eaaf5c3e43d5f8faad4a8794cb987e9b03745c78dd'
                      '919512183898dfbecd52e2408e43871fd021109117bd3ed4eaf8437743715d4f'),
        dict(
            type=ProvisioningPDUType.PUBLIC_KEY,
            parameters=dict(
                key=ecdsa.VerifyingKey.from_string(
                    bytes.fromhex('2c31a47b5779809ef44cb5eaaf5c3e43d5f8faad4a8794cb987e9b03745c78dd'
                                  '919512183898dfbecd52e2408e43871fd021109117bd3ed4eaf8437743715d4f'),
                    curve=ecdsa.NIST256p,
                )
            )
        ),
        id="public key",
    ),
])
def test_parse_key(encoded, decoded):
    result = ProvisioningPDU.parse(data=encoded)
    assert result['type'] == decoded['type']
    assert result['parameters']['key'].to_pem() == decoded['parameters']['key'].to_pem()


def test_confirmation():
    invite = bytes.fromhex('00')
    capabilities = bytes.fromhex('0100010000000000000000')
    start = bytes.fromhex('0000000000')
    provisioner_key = bytes.fromhex(
        '2c31a47b5779809ef44cb5eaaf5c3e43d5f8faad4a8794cb987e9b03745c78dd'
        '919512183898dfbecd52e2408e43871fd021109117bd3ed4eaf8437743715d4f'
    )
    device_key = bytes.fromhex(
        'f465e43ff23d3f1b9dc7dfc04da8758184dbc966204796eccf0d6cf5e16500cc'
        '0201d048bcbbd899eeefc424164e33c201c2b010ca6b4d43a8a155cad8ecb279'
    )
    ecdh_secret = bytes.fromhex(
        'ab85843a2f6d883f62e5684b38e307335fe6e1945ecd19604105c6f23221eb69'
    )
    random = bytes.fromhex('8b19ac31d58b124c946209b5db1021b9')

    auth = None

    salt, confirmation = \
        provisioning_confirmation(
            ecdh_secret,
            invite + capabilities + start + provisioner_key + device_key,
            random,
            auth)

    assert confirmation == bytes.fromhex('b38a114dfdca1fe153bd2c1e0dc46ac2')
