"""
Tests for SIAP and SSAP renderers.
"""

#c Copyright 2008-2025, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


import atexit
import io
import json
import struct

from gavo.helpers import trialhelpers
from gavo import api
from gavo.utils import fitstools


class SSATest(trialhelpers.ArchiveTest):
	def testMetadataFormat(self):
		return self.assertGETHasStrings("/data/ssatest/c/ssap.xml",
			{"FORMAT": "Metadata", "REQUEST": "queryData"},
			["<VOTABLE", 'name="QUERY_STATUS" value="OK"', 'name="INPUT:SIZE"']
			).addCallback(self.assertResponseIsValid)

	def testProtocolDeclared(self):
		return self.assertGETHasStrings("/data/ssatest/c/ssap.xml",
			{"REQUEST": "queryData"},
			['<INFO name="standardID" value="ivo://ivoa.net/std/ssap"']
			).addCallback(self.assertResponseIsValid)

	def testEmptyResponseWorks(self):
		return self.assertGETHasStrings("/data/ssatest/c/ssap.xml",
			{"REQUEST": "queryData", "POS": "234.3920,-34.302", "SIZE": "0.0001"}, [
				'name="QUERY_STATUS" value="OK"',
				'utype="stc:AstroCoords.Position2D.Value2"/'])


class HTTPSTest(trialhelpers.ArchiveTest):
	def _makeSecure(self, request):
		request.secure = True

	def testPSProductLink(self):
		return self.assertGETHasStrings("/data/ssatest/c/ssap.xml",
			{"REQUEST": "queryData", "POS": "10,15", "SIZE": "2"}, [
				"<TD>https://localhost/getproduct/data/spec2.ssatest.vot",
				'value="https://localhost/data/ssatest/dl/dlget"'
			], rm=self._makeSecure)


class SIAP2Test(trialhelpers.ArchiveTest):
	def testUsageFault(self):
		return self.assertGETHasStrings("/__system__/siap2/sitewide/siap2.xml",
			{"POS": "Forgot about this shitty syntax"}, [
				"UsageFault: Field POS: Invalid SIAPv2 geometry",
				"<VOTABLE",
				'RESOURCE type="results"',
				'value="ERROR"']
			).addCallback(self.assertResponseIsValid)

	def testRegularQuery(self):
		return self.assertGETHasStrings("/data/siap2test/svc/siap2.xml",
			{"TIME": "54250 54260", "RESPONSEFORMAT": "votabletd"}, [
				# ensure basic, VOTable-serialised metadata comes back
				'value="http://localhost:8080/tableinfo/test.siap2#ti-citing"',
				# ensure at least one data product, with accref url-expanded, comes back
				'<TD>http://localhost:8080/getproduct/data/ex.fits</TD>',
				'<INFO name="request"', # this and next: Data Origin
				'svc/siap2.xml?TIME=54250+54260&amp;RESPONSEFORMAT=votabletd"',
				])


class SIAPTest(trialhelpers.ArchiveTest):
	def assertColumnLinkSatisfied(self, siapResponse):
		tree = trialhelpers.getXMLTree(siapResponse[0])
		dlDesc = tree.xpath("RESOURCE[@utype='adhoc:service']")[0]
		idSrc = dlDesc.xpath("GROUP/PARAM[@name='ID']")[0].get("ref")
		refField = tree.xpath("//*[@ID='%s']"%idSrc)[0]
		self.assertEqual(refField.get("name"), "accref")
		self.assertEqual(refField.tag, "FIELD")
		self.assertEqual(refField.getparent(
			).getparent().get("type"), "results")
		return siapResponse

	def testEmbeddedDatalinkResource(self):
		return self.assertGETHasStrings("/data/test/pgsiapsvc/siap.xml",
			{"POS": ["0, 0"], "SIZE": ["90, 90"]},
			['utype="adhoc:service"', "datalink#links-1.1"]
			).addCallback(self.assertColumnLinkSatisfied
			).addCallback(self.assertResponseIsValid)

	def testMetadataResponse(self):
		return self.assertGETHasStrings("/data/test/pgsiapsvc/siap.xml",
			{"FORMAT": "METADATA"},
			["<PARAM", "INPUT:POS", 'ucd="VOX:Image_AccessReference"',
			'name="FITS Binary Table"', 'name="INPUT:MAXREC"', 'value="21231"'])


class ProductRenderTest(trialhelpers.ArchiveTest):
	def testGetNotFound(self):
		def assert404(result):
			self.assertEqual(result[1].code, 404)

		return self.assertGETHasStrings("/getproduct/sonstwo/ex.fits",
			{}, [
			"Not Found (404)",
			'<div class="errmsg">No dataset with accref sonstwo/ex.fits known here.'
			]).addCallback(assert404)

	def testGetFile(self):
		return self.assertGETHasStrings("/getproduct/data/ex.fits",
			{}, [
			"NAXIS1  =                   12",
			"\0\0\0\0\0\0\0\0\0\0"])

	def testGetCutout(self):
		return self.assertGETHasStrings("/getproduct/data/ex.fits", {
				"ra": "168.24529", "dec": "22.21588",
				"sra":"0.0004", "sdec": "0.0001"},
			["NAXIS1  =                    2", "NAXIS2  =                    1",
			"V\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"])
	
	def testFailingPreview(self):
		def assertCode(res):
			# the gobbledigook is from our productrender-built-in error PNG
			self.assertTrue(b"\x75\x69\xef\x0d\x45" in res[0])
			self.assertEqual(res[1].code, 404)

		return self.assertGETHasStrings("/getproduct/data/spec3.ssatest",
			{"preview": "True"},
			["PNG"]
			).addCallback(assertCode)

	def testForbidden(self):
		target = api.getConfig("inputsDir") / "data" / "ex.fits"
		def cleanup(res):
			target.chmod(0o644)
			return res

		target.chmod(0o000)
		return self.assertGETHasStrings("/getproduct/data/ex.fits", {},
			["<title>403 - Forbidden Resource</title>"]).addBoth(cleanup)


class MangledFITSProductsTest(trialhelpers.ArchiveTest):
	def testScaledFITS(self):
		def assertions(res):
			resFile = io.BytesIO(res[0])
			hdr = fitstools.readPrimaryHeaderQuick(resFile)
			self.assertEqual(hdr["NAXIS1"], 4)
			self.assertEqual(hdr["BITPIX"], -32)
			self.assertTrue("getproduct/data/ex.fits" in hdr["FULLURL"])
			self.assertAlmostEqual(
				struct.unpack("!f", resFile.read(4))[0],
				7437.5556640625)

		return self.assertGETHasStrings("/getproduct/data/ex.fits",
			{"scale": "3"}, [
			"SIMPLE"]).addCallback(assertions)

	def testCutoutFITS(self):
		def assertions(res):
			self.assertTrue(b"NAXIS1  =                    4" in res[0])
			self.assertTrue(b"NAXIS2  =                    5" in res[0])
			self.assertTrue(b" \xa8D\xaaG" in res[0])

		return self.assertGETHasStrings("/getproduct/data/ex.fits",
			{"ra": "168.24511", "dec": "22.214493", "sra": "0.001", "sdec": "0.001"},
			["SIMPLE"]).addCallback(assertions)

	def testPreviewFITS(self):
		return self.assertGETHasStrings("/getproduct/data/ex.fits",
			{"preview": "1"},
			["JFIF"])

	def testPreviewCutout(self):
		return self.assertGETHasStrings("/getproduct/data/ex.fits", {
			"ra": "168.24572", "dec": "22.214473",
			"sra": "0.005", "sdec": "0.005",
			"preview": "1"},
			["JFIF"])


class BiblinksTest(trialhelpers.ArchiveTest):
	def testBasic(self):
		return self.assertGETHasStrings(
			"/__system__/biblinks/links/biblinks.json", {}, [
				'{"bib-ref": "2015A&C....10...88D",',
				'"relationship": "Cites",',
				'"bib-format": "doi"',
				'"cardinality": 5']
			).addCallback(self._assertBasicProps)

	def _assertBasicProps(self, result):
		self.assertEqual(result[1].responseHeaders.getRawHeaders("content-type")[0],
			'application/json')
		links = json.loads(result[0])

		self.assertEqual(set(r["bib-ref"] for r in links if r["dataset-ref"]=='http://reg.g-vo.org/LP/x-testing/data/testdata/haslinks'),
			{'2015A&C....10...88D', '2005ASPC..347...29T'})

		return result


atexit.register(trialhelpers.provideRDData("ssatest", "test_import"))
atexit.register(trialhelpers.provideRDData("test", "pgs_siaptest"))
atexit.register(trialhelpers.provideRDData("test", "import_fitsprod"))
atexit.register(trialhelpers.provideRDData("siap2test", "import"))
atexit.register(trialhelpers.provideRDData("testdata", "import-biblinks"))
