'''
The first service in operation that annotates query responses in the fly is Vizier
https://cds/viz-bin/mivotconesearch/VizierParams
Data are mapped on the mango:EpochPropagtion class as it is implemented in the current code.
This test case is based on 2 VOTables:
Both tests check the generation of SkyCoord instances from the MivotInstances built
for the output of this service.
'''
import pytest
from copy import deepcopy
from astropy.utils.data import get_pkg_data_filename
from astropy import units as u
from pyvo.mivot.version_checker import check_astropy_version
from pyvo.mivot.viewer.mivot_instance import MivotInstance
from pyvo.mivot.features.sky_coord_builder import SkyCoordBuilder
from pyvo.mivot.utils.exceptions import NoMatchingDMTypeError
from pyvo.mivot.viewer.mivot_viewer import MivotViewer
from pyvo.utils import activate_features

# Enable MIVOT-specific features in the pyvo library
activate_features("MIVOT")

# annotations generated by Vizier as given to the MivotInstance
vizier_dict = {
    "dmtype": "mango:EpochPosition",
    "longitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 52.26722684,
        "unit": "deg",
        "ref": "RAICRS",
    },
    "latitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 59.94033461,
        "unit": "deg",
        "ref": "DEICRS",
    },
    "pmLongitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -0.82,
        "unit": "mas/yr",
        "ref": "pmRA",
    },
    "pmLatitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -1.85,
        "unit": "mas/yr",
        "ref": "pmDE",
    },
    "obsDate": {
        "dmtype": "ivoa:RealQuantity",
        "value": 1991.25,
        "unit": "yr",
        "ref": None,
    },
    "spaceSys": {
        "dmtype": "coords:SpaceSys",
        "dmid": "SpaceFrame_ICRS",
        "dmrole": "coords:Coordinate.coordSys",
        "frame": {
            "dmtype": "coords:SpaceFrame",
            "dmrole": "coords:PhysicalCoordSys.frame",
            "spaceRefFrame": {
                "dmtype": "ivoa:string",
                "value": "ICRS"
            },
        },
    },
}
# The same edited by hand (parallax added and FK5 + Equinox frame)
vizier_equin_dict = {
    "dmtype": "mango:EpochPosition",
    "longitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 52.26722684,
        "unit": "deg",
        "ref": "RAICRS",
    },
    "latitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 59.94033461,
        "unit": "deg",
        "ref": "DEICRS",
    },
    "pmLongitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -0.82,
        "unit": "mas/yr",
        "ref": "pmRA",
    },
    "pmLatitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -1.85,
        "unit": "mas/yr",
        "ref": "pmDE",
    },
    "parallax": {
        "dmtype": "ivoa:RealQuantity",
        "value": 0.6,
        "unit": "mas",
        "ref": "parallax",
    },
    "obsDate": {
        "dmtype": "ivoa:RealQuantity",
        "value": 1991.25,
        "unit": "yr",
        "ref": None,
    },
    "spaceSys": {
        "dmtype": "coords:SpaceSys",
        "dmid": "SpaceFrame_ICRS",
        "dmrole": "coords:Coordinate.coordSys",
        "frame": {
            "dmtype": "coords:SpaceFrame",
            "dmrole": "coords:PhysicalCoordSys.frame",
            "spaceRefFrame": {
                "dmtype": "ivoa:string",
                "value": "FK5"
            },
            "equinox": {
                "dmtype": "coords:SpaceFrame.equinox",
                "value": "2012",
                "unit": "yr",
            }
        }
    },
}

# The same edited mapped on a dummy class
vizier_dummy_type = {
    "dmtype": "mango:DumyType",
    "longitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 52.26722684,
        "unit": "deg",
        "ref": "RAICRS",
    },
    "latitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": 59.94033461,
        "unit": "deg",
        "ref": "DEICRS",
    },
    "pmLongitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -0.82,
        "unit": "mas/yr",
        "ref": "pmRA",
    },
    "pmLatitude": {
        "dmtype": "ivoa:RealQuantity",
        "value": -1.85,
        "unit": "mas/yr",
        "ref": "pmDE",
    },
    "parallax": {
        "dmtype": "ivoa:RealQuantity",
        "value": 0.6,
        "unit": "mas",
        "ref": "parallax",
    },
    "obsDate": {
        "dmtype": "ivoa:RealQuantity",
        "value": 1991.25,
        "unit": "yr",
        "ref": None,
    },
    "coordSys": {
        "dmtype": "coords:SpaceSys",
        "dmid": "SpaceFrame_ICRS",
        "dmrole": "coords:Coordinate.coordSys",
        "spaceRefFrame": {
            "dmtype": "coords:SpaceFrame.spaceRefFrame",
            "value": "FK5",
            "unit": None,
            "ref": None,
        },
        "equinox": {
            "dmtype": "coords:SpaceFrame.equinox",
            "value": "2012",
            "unit": "yr",
        },
    },
}


def test_no_matching_mapping():
    """
    Test that a NoMatchingDMTypeError is raised not mapped on mango:EpochPosition
    """
    with pytest.raises(NoMatchingDMTypeError):
        mivot_instance = MivotInstance(**vizier_dummy_type)
        scb = SkyCoordBuilder(mivot_instance)
        scb.build_sky_coord()


@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+")
def test_vizier_output():
    """ Test the SkyCoord issued from the Vizier response
    """
    mivot_instance = MivotInstance(**vizier_dict)
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
               "(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")
    scoo = mivot_instance.get_SkyCoord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
               "(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")

    vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "Galactic"
    mivot_instance = MivotInstance(**vizier_dict)
    scoo = mivot_instance.get_SkyCoord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (Galactic): (l, b) in deg(52.26722684, 59.94033461) "
               "(pm_l_cosb, pm_b) in mas / yr(-0.82, -1.85)>")

    vizier_dict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "QWERTY"
    mivot_instance = MivotInstance(**vizier_dict)
    scoo = mivot_instance.get_SkyCoord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (ICRS): (ra, dec) in deg(52.26722684, 59.94033461) "
               "(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")


@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+")
def test_vizier_output_with_equinox_and_parallax():
    """Test the SkyCoord issued from the modified Vizier response *
    (parallax added and FK5 + Equinox frame)
    """
    mivot_instance = MivotInstance(**vizier_equin_dict)
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (FK5: equinox=J2012.000): (ra, dec, distance) in "
               "(deg, deg, pc)(52.26722684, 59.94033461, 1666.66666667) "
               "(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")

    mydict = deepcopy(vizier_equin_dict)
    mydict["spaceSys"]["frame"]["spaceRefFrame"]["value"] = "FK4"
    mivot_instance = MivotInstance(**mydict)
    scoo = mivot_instance.get_SkyCoord()
    assert (str(scoo).replace("\n", "").replace("  ", "")
            == "<SkyCoord (FK4: equinox=B2012.000, obstime=B1991.250): (ra, dec, distance) in "
               "(deg, deg, pc)(52.26722684, 59.94033461, 1666.66666667) "
               "(pm_ra_cosdec, pm_dec) in mas / yr(-0.82, -1.85)>")


@pytest.mark.skipif(not check_astropy_version(), reason="need astropy 6+")
def test_simad_cs_output():
    """Test the SkyCoord issued from a Simbad SCS response
    """
    filename = get_pkg_data_filename('data/simbad-cone-mivot.xml')
    m_viewer = MivotViewer(filename, resolve_ref=True)
    mivot_instance = m_viewer.dm_instance
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()

    assert scoo.ra.degree == pytest.approx(269.45207696)
    assert scoo.dec.degree == pytest.approx(4.69336497)
    assert scoo.distance.pc == pytest.approx(1.82823411)
    x = scoo.pm_ra_cosdec.value
    y = (-801.551 * u.mas/u.yr).value
    assert x == pytest.approx(y)
    x = scoo.pm_dec.value
    y = (10362.394 * u.mas/u.yr).value
    assert x == pytest.approx(y)
    assert str(scoo.obstime) == "J2000.000"


def test_time_representation():
    """
    Test various time representations
    Inconsistent values are not tested since there are detected by ``astropy.core.Time``
    """
    # work with a copy to not alter other test functions
    mydict = deepcopy(vizier_equin_dict)
    mydict["obsDate"]["unit"] = "mjd"
    mivot_instance = MivotInstance(**mydict)
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()
    assert scoo.obstime.jyear_str == "J1864.331"

    mydict["obsDate"]["unit"] = "jd"
    mydict["obsDate"]["value"] = "2460937.36"
    mivot_instance = MivotInstance(**mydict)
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()
    assert scoo.obstime.jyear_str == "J2025.715"

    mydict = deepcopy(vizier_equin_dict)
    mydict["obsDate"]["unit"] = "iso"
    mydict["obsDate"]["dmtype"] = "ivoa:string"
    mydict["obsDate"]["value"] = "2025-05-03"
    mivot_instance = MivotInstance(**mydict)
    scb = SkyCoordBuilder(mivot_instance)
    scoo = scb.build_sky_coord()
    assert scoo.obstime.jyear_str == "J2025.335"
