/*
This is part of datacrate, a node.js library for working with 
DataCrates.  Copyright (C) 2018  University of Technology Sydney

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

const chai = require('chai');
const chaiFiles = require('chai-files');
chai.use(chaiFiles);
const _ = require('lodash');
const assert = chai.assert;
const expect = chai.expect;
const file = chaiFiles.file;

const fs = require('fs-extra');

const catalog = require('../lib/catalog.js');

const ORG = { 
	'id': 'https://www.uts.edu.au',
	'name': 'University of Technology Sydney'
};

// defining these here so that the tests know what to 
// look up in the results @graph

const IRI_PREFIXES = {
	'about': {
	    'dc:subject_anzsrc:for': '_:FOR/',
    	'dc:subject_anzsrc:seo': '_:SEO/'
	},
	'spatialCoverage': '_:spatial/',
	'funder': '_:funder/',
	'licence': '_:licence/',
	'citation': '_:citation/'
};


const OWNER = 'https://orcid.org/0000-0001-5152-5307';
const APPROVER = 'admin';

const DATASET_ID = 'DATASET_ID';

// get catalog item by id: returns null if the item isn't unique, because
// this should always make the test fail

function get_id(c, id) {
	const match = c['@graph'].filter((item) => item['@id'] === id);
	if( match.length ) {
		if( match.length > 1 ) {
			console.error(`Warning: catalog has ${match.length} items with ID ${id}`);
			return null;
		} else {
			return match[0];
		}
	} else {
		return null;
	}
}


// this is for fetching all of the items with the prefix for
// spatialCoverage items, say.

function get_id_prefix(c, prefix) {
	return c['@graph'].filter((item) => item['@id'].startsWith(prefix));
}

// this one returns an array of items with the requested type

function get_type(c, t) {
	return c['@graph'].filter((item) => item['@type'] === t);
}




var mdp, dp, cj, cjds;


// TODO: it would be great if these could be run against a
// range of different test datapubs which didn't have all of
// the values

describe("Convert a ReDBox 2.0 DataPub to CATALOG.json", () => {

	before(async () => {
		mdp = await fs.readJson('./test_data/datapub.json');
		dp = mdp['metadata'];
		cj = await catalog.datapub2catalog({
			'id': DATASET_ID,
			'datapub': dp,
			'organisation': ORG,
			'owner': OWNER,
			'approver': APPROVER,
			'prefixes': IRI_PREFIXES
		});
		const roots = cj['@graph'].filter((item) => {return item['path'] === 'data/'});
		cjds = roots[0];
		await fs.writeJson('./test_data/CATALOG.json', cj, { 'spaces': 4 });
	});

	it("has a root dataset", () => {
  		expect(cjds).to.be.a('object');
	});

	it("has string @id values for every graph item", () => {
		cj['@graph'].map((i) => {
			expect(i).to.have.property('@id');
			expect(i['@id']).to.be.a('string');
		})
	});


	it("has a dataset item with correct metadata", () => {
		assert(cj, "Got an object");
		assert(cj['@graph'], "Catalog has a @graph");
		const dataset = get_id(cj, DATASET_ID);
		assert(dataset, "Graph has an item with id " + DATASET_ID);
		expect(dataset['name']).to.equal(dp['title']);
		expect(dataset['description']).to.equal(dp['description']);
		expect(dataset['publisher']['@id']).to.equal(ORG['id']);
		if( dp['finalKeywords']) {
			expect(dataset['keywords']).to.equal(dp['finalKeywords'].join(', '));
		}
	});

	it("has a create action with the owner", () => {
		const cas = get_type(cj, 'CreateAction');
		expect(cas).to.have.length(1);
		const ca = cas[0];
		expect(ca['agent']['@id']).to.equal(OWNER);
	});

	it("has an update action with the approver", () => {
		const uas = get_type(cj, 'UpdateAction');
		expect(uas).to.have.length(1);
		const ua = uas[0];
		expect(ua['agent']['@id']).to.equal(APPROVER);
	});

	it("has the payload files", () => {
		const files = get_type(cj, "File");
		const datalocs = dp['dataLocations'];
		expect(files).to.have.length(datalocs.length);
		const fids = files.map((f) => f['@id']).sort();
		const dlids = datalocs.map((dl) => dl['name']).sort();
		expect(fids).to.eql(dlids);
	});


	it("has subjects", () => {
		// FORs and SEOs
		const fields = [ 'dc:subject_anzsrc:for', 'dc:subject_anzsrc:seo'];

		const about = cjds['about'].map((i) => i['@id']);
		_.forEach(fields, (field) => {
			const pref = IRI_PREFIXES['about'][field];
			const expectsubs = dp[field].map((f) => f['name']);
			const gotsubs = get_id_prefix(cj, pref).map((f) => f['name']);
			if( dp[field] && dp[field].length > 0 ) {
				expect(gotsubs.sort()).to.eql(expectsubs.sort());
			} else {
				expect(gotsubs).to.be.empty;
			}
		})
	});

	// geolocations - basic_name / latitude / longitude

	// this test assumes that each geolocation's name is unique in the
	// datapub

	// and that geospatial is always a FeatureCollection 

	it("has spatial coverage", () => {
		const spatials = get_id_prefix(cj, IRI_PREFIXES['spatialCoverage']);
		if( dp['geoLocations'] ) {
			dp['geoLocations'].map((gl) => {
				const name = gl['basic_name'];
				const matches = spatials.filter((s) => s['name'] === name );
				expect(matches).to.have.lengthOf(1);
				expect(matches[0]['latitude']).to.equal(gl['latitude']);
				expect(matches[0]['longitude']).to.equal(gl['longitude']);
			});
		}
		if( dp['geospatial']) {
			const matches = spatials.filter((s) => s['@type'] === 'FeatureCollection' );
			expect(matches).to.have.lengthOf(1);
			expect(matches[0]['features']).to.eql(dp['geospatial']['features']);			
		}
	});



	it("has temporal coverage", () => {
		var tc = '';
		if( dp['startDate'] ) {
			tc = dp['startDate'];
			if( dp['endDate'] ) {
				tc += '/' + dp['endDate'];
			}
		} else {
			if( dp['endDate']) {
				tc = dp['endDate'];
			}
		}
		if( dp['timePeriod'] ) {
			if( tc ) {
				tc += '; ' + dp['timePeriod'];
			} else {
				tc = dp['timePeriod'];
			}
		}
		if( tc ) {
			expect(cjds['temporalCoverage']).to.equal(tc)
		} else {
			expect(cjds).not.to.have.property('temporalCoverage');
		}
	});

	it("has funders", () => {
		assert(false, "Not written yet");
	});

	it("has a licence", () => {
		const licenses = cjds['license'];
		if( dp['license_other_url'] || dp['license_identifier'] ) {
			expect(licenses).to.be.an('array');
			const license = licenses[0];
			const id = license['@id'];
			const litem = get_id(cj, id);
			expect(litem).to.be.a('object');
			if( dp['license_other_url']) {
				expect(id).to.equal(dp['license_other_url']);
				expect(litem['name']).to.equal(dp['license_notes']);
			} else {
				expect(id).to.equal(dp['license_identifier']);
				expect(litem['name']).to.equal(dp['license_statement']);
				expect(litem['url]']).to.equal(dp['license_statement_url']);
			}
		}
	});

	it("has related works", () => {
		assert(false, "Not written yet");
	});


});