import pytest

import fin_defs

NVO_ID = 'stock:NVO.NYSE{nordnet_id=16256554}'
NVO = fin_defs.Asset.from_string_id(NVO_ID)

VALID_IDS = [
    'stock:NVO.NYSE',
    NVO_ID,
    'currency:USD',
    'fiat:USD',
    'index:NDX',
    'crypto:BTC',
    'crypto:BTC{coingecko_id=bitcoin}',
    'crypto:MPC',
    'crypto:LOLCOIN',
    'commodity:ALUMINUM',
    'unknown:XXX',
    'crypto:    ',  # CCXT actually found one with only whitespace. Why?
]


ASSETS = list(fin_defs.WELL_KNOWN_SYMBOLS.values()) + [
    fin_defs.Asset.from_string_id(vid) for vid in VALID_IDS
]


ASSETS_POLYGON_PRESERVES_FULL_ID = frozenset(
    a
    for a in ASSETS
    if not isinstance(
        a,
        fin_defs.CryptoCurrency | fin_defs.Commodity | fin_defs.UnknownAsset,
    )
) - frozenset([NVO])

INVALID_IDS = [
    'NVO',
    'NVO.NYSE',
    'test:test',
    'fiat:TEST:TEST',
    'index:TEST:TEST',
    'commodity:TEST:TEST',
    '!!!!',
    '::::',
    '',
    '          ',
]


def test_parse_attr():
    assert fin_defs.parse_id_attr_data('') == {}
    assert fin_defs.parse_id_attr_data('     ') == {}
    assert fin_defs.parse_id_attr_data('abc=abc') == {'abc': 'abc'}
    assert fin_defs.parse_id_attr_data('abc=123') == {'abc': 123}
    assert fin_defs.parse_id_attr_data('abc=123,xyz=abc') == {'abc': 123, 'xyz': 'abc'}
    assert fin_defs.parse_id_attr_data('    abc=123    ,    xyz=abc    ') == {
        'abc': 123,
        'xyz': 'abc',
    }


def test_from_nordnet():
    asset = NVO
    assert isinstance(asset, fin_defs.Stock)
    assert asset.ticker == 'NVO'
    assert asset.nordnet_id == 16256554


@pytest.mark.parametrize('asset', ASSETS)
def test_raw_short_name(asset: fin_defs.Asset):
    assert asset.raw_short_name() is not None


@pytest.mark.parametrize('asset_id', VALID_IDS)
def test_from_string_id(asset_id: str):
    assert fin_defs.Asset.from_string_id(asset_id)


@pytest.mark.parametrize('asset_id', INVALID_IDS)
def test_from_string_id_invalid(asset_id: str):
    with pytest.raises(ValueError, match='.*format.*'):
        fin_defs.Asset.from_string_id(asset_id)


@pytest.mark.parametrize('asset', ASSETS)
def test_to_from_string_id(asset: fin_defs.Asset):
    assert fin_defs.Asset.from_string_id(asset.to_string_id()) == asset


@pytest.mark.parametrize('asset', ASSETS)
def test_to_from_polygon_id_works(asset: fin_defs.Asset):
    if isinstance(asset, fin_defs.Commodity | fin_defs.UnknownAsset):
        return
    assert fin_defs.Asset.from_polygon_id(asset.to_polygon_id()) is not None


@pytest.mark.parametrize('asset', ASSETS_POLYGON_PRESERVES_FULL_ID)
def test_to_from_polygon_id_preserves_id(asset: fin_defs.Asset):
    assert fin_defs.Asset.from_polygon_id(asset.to_polygon_id()) == asset


def test_to_polygon_id_fail_for_commodity():
    with pytest.raises(TypeError, match='Unknown type: Commodity'):
        assert fin_defs.Commodity.CORN.to_polygon_id()


def test_to_string_id_wrong_type():
    with pytest.raises(TypeError, match='Unsupported asset type: int'):
        assert fin_defs.Asset.to_string_id(1)