<?php

use PHPUnit\Framework\TestCase;

class ExiSynchrotronTest extends TestCase {

    public static array $exiSynchrotrons;

    public static array $authTokens=array();

    const HANDLER='EXI';

    /**
     * @throws BadRequestException
     * @throws NotFoundException
     * @throws ServerException
     */
    public function setUp():void {
        database::connect();
        session::init(new DummySession());
        session::becomeAdmin();
        $user=user::getFirstAdmin();
        session::set('user', $user);
        session::set('userId', $user['id']);
        session::revertAdmin();
    }

	public static function getSynchrotrons(): array {
		if(!isset(static::$exiSynchrotrons)){
			$config=@file_get_contents(__DIR__.'/../../../../testconfig.json');
			$config=json_decode($config,true);
			foreach ($config['synchrotrons'] as $synchrotron){
				if($synchrotron['handler']===self::HANDLER){
					static::$exiSynchrotrons[]=array($synchrotron);
				}
			}
		}
		return static::$exiSynchrotrons;
	}

    /**
     * @dataProvider getSynchrotrons
     * @param $synchrotron
     */
    public function testHandlerIsCorrect($synchrotron){
        self::assertEquals(self::HANDLER, $synchrotron['handler'], $synchrotron['name'].' is not an EXI synchrotron');
    }

    /**
     * @dataProvider getSynchrotrons
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testAuthenticate($synchrotron){
        $synchrotronName=$synchrotron['name'];
		$url=$synchrotron['baseuri'].'authenticate?site='.$synchrotronName;
		echo $url;
        $response=corsproxy::doPost(
            $url,
            array('rawPostBody'=>'login='.$synchrotron['user'].'&password='.urlencode($synchrotron['pass']))
        );
		self::assertNotEmpty($response);
        $responseJson=json_decode($response, true);
		self::assertNotNull($responseJson, "Non-JSON response from $synchrotronName:\n$response");
        self::assertArrayHasKey('token', $responseJson, $synchrotronName.' response does not have an auth token');
        static::$authTokens[$synchrotron['name']]=$responseJson['token'];
    }

    /**
     * @dataProvider getSynchrotrons
     * @depends      testAuthenticate
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testGetProposals($synchrotron){
        $response=$this->httpGet($synchrotron, 'proposal/list', true);
        $response=self::checkResponseIsJson($response, $synchrotron, false);
        $found=false;
        $synchrotronProposal=strtolower($synchrotron['proposal']);
        foreach($response as $proposal){
            if(strtolower($proposal['Proposal_proposalCode']).$proposal['Proposal_proposalNumber']==$synchrotronProposal){
                $found=true;
            }
        }
        self::assertTrue($found, 'Proposal used for test ('.$synchrotronProposal.') not found in proposal list from '.$synchrotron['name']);
    }

    /**
     * @dataProvider getSynchrotrons
     * @depends      testAuthenticate
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testGetSessionsForShipment($synchrotron){
        $synchrotronName=$synchrotron['name'];
        $response=$this->httpGet($synchrotron, 'shipping/'.$synchrotron['ispybShipmentId'].'/get');
        $response=self::checkResponseIsJson($response, $synchrotron, false);
		echo $response;
        self::assertArrayHasKey('sessions', $response, 'Shipment does not have "sessions" key');
        self::assertNotEmpty($response['sessions']);
        self::assertArrayHasKey('sessionId', $response['sessions'][0], 'Shipment session does not have "sessionId" key');
        $sessionIds=array_column($response['sessions'], 'sessionId');
        self::assertCount($synchrotron['expectedSessionCount'], $sessionIds, 'Wrong number of sessions for shipment');
        self::assertTrue(in_array($synchrotron['ispybSessionId'], $sessionIds), $synchrotronName.': Test session ID not found in shipment');
    }

    /**
     * @dataProvider getSynchrotrons
     * @depends      testAuthenticate
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testGetDataCollectionsForSession($synchrotron){
        $response=$this->httpGet($synchrotron, 'mx/datacollection/session/'.$synchrotron['ispybSessionId'].'/list');
        $response=$this->checkResponseIsJson($response, $synchrotron, false);
        self::assertCount($synchrotron['expectedDataCollectionCountForSession'], $response);
        $sampleIds=array_column($response,'BLSample_blSampleId');
        self::assertTrue(in_array($synchrotron['ispybSampleId'], $sampleIds));
        self::assertArrayHasKey('DataCollectionGroup_dataCollectionGroupId', $response[0]);
    }

    /**
     * @dataProvider getSynchrotrons
     * @depends      testAuthenticate
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testGetDataCollectionsForDataCollectionGroupId($synchrotron){
        $response=$this->httpGet($synchrotron, 'mx/datacollection/datacollectiongroupid/'.$synchrotron['ispybDataCollectionGroupId'].'/list');
        $response=$this->checkResponseIsJson($response, $synchrotron, false);
        //echo $synchrotronName.': '.count($response)." data collections\n";
        self::assertCount($synchrotron['expectedDataCollectionCountForGroup'], $response);
        $dataCollectionIds=array_column($response,'dataCollectionId');
        self::assertTrue(in_array($synchrotron['ispybDataCollectionId'], $dataCollectionIds));
    }

    /**
     * @dataProvider getSynchrotrons
     * @depends      testAuthenticate
     * @param $synchrotron
     * @throws BadRequestException
     * @throws ForbiddenException
     * @throws ServerException
     */
    public function testGetDataCollectionsForSample($synchrotron){
        $response=$this->httpGet($synchrotron, 'mx/datacollection/sample/'.$synchrotron['ispybSampleId'].'/list');
        $response=$this->checkResponseIsJson($response, $synchrotron, false);
        //echo $synchrotron['name'].': '.count($response)." data collections for sample";
        self::assertCount($synchrotron['expectedDataCollectionCountForSample'], $response);
        echo $synchrotron['name'].': '.'mx/datacollection/sample/'.$synchrotron['ispybSampleId'].'/list'."\n".json_encode($response, JSON_PRETTY_PRINT);
    }

    /**
     * @throws ServerException
     * @throws BadRequestException
     * @throws ForbiddenException
     */
    protected function httpGet($synchrotron, $path, $suppressProposal=false): bool|string {
        $synchrotronName=$synchrotron['name'];
        self::checkAuthTokenPresent($synchrotronName);
        $token=static::$authTokens[$synchrotronName];
        $url=$synchrotron['baseuri'].$token;
        if(!$suppressProposal){
            $url.= '/proposal/'.$synchrotron['proposal'];
        }
        $url.='/'.$path;
        return corsproxy::doGet(
            $url,
            ['httpHeaders'=>['User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/53']]
        );
    }

    protected function checkAuthTokenPresent($synchrotronName): void {
        self::assertArrayHasKey($synchrotronName, static::$authTokens, 'Auth token not saved for '.$synchrotronName);
    }

    protected function checkResponseIsJson($response, $synchrotron, $emptyOK){
        $synchrotronName=$synchrotron['name'];
        self::assertNotEmpty($response, $synchrotronName.' returned an empty string - not even {} or []');
        $decoded=json_decode($response, true);
        self::assertNotNull($decoded, $synchrotronName.' returned a non-JSON response: '.$response);
        if(!$emptyOK){
            self::assertNotEmpty($decoded, $synchrotronName.' returned empty JSON, which the test says is not OK');
        }
        return $decoded;
    }

}