import { createStubTitle } from '../stubs';
import { createModel } from '../../../src/preview/model';
import createRESTBaseGateway from '../../../src/gateway/rest';

const DEFAULT_CONSTANTS = {
		THUMBNAIL_SIZE: 512,
		endpoint: '/api/rest_v1/page/summary/'
	},
	RESTBASE_RESPONSE = {
		type: 'standard',
		title: 'Barack Obama',
		extract: 'Barack Hussein Obama II born August 4, 1961) ...',
		thumbnail: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/200px-President_Barack_Obama.jpg',
			width: 200,
			height: 250
		},
		originalimage: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg',
			width: 800,
			height: 1000
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-01-30T10:17:41Z',
		description: '44th President of the United States of America'
	},
	SVG_RESTBASE_RESPONSE = {
		type: 'standard',
		title: 'Barack Obama',
		extract: 'Barack Hussein Obama II born August 4, 1961) ...',
		thumbnail: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.svg/200px-President_Barack_Obama.svg.png',
			width: 200,
			height: 250
		},
		originalimage: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.svg',
			width: 800,
			height: 1000
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-01-30T10:17:41Z',
		description: '44th President of the United States of America'
	},
	RESTBASE_RESPONSE_WITHOUT_IMAGE = {
		type: 'standard',
		title: 'Barack Obama',
		extract: 'Barack Hussein Obama II born August 4, 1961) ...',
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-01-30T10:17:41Z',
		description: '44th President of the United States of America'
	},

	// Note well that the thumbnail and originalimage properties are
	// identical. This can be the case where RESTBase requests a thumbnail at
	// 320px width but the original image isn't that wide, in which instance
	// PageImages - and therefore RESTBase - return the original image as the
	// thumbnail.
	//
	// See https://phabricator.wikimedia.org/T158632#3071104 onward for additional
	// context.
	RESTBASE_RESPONSE_WITH_SMALL_IMAGE = {
		type: 'standard',
		title: 'PreviewsNonFreeImage/sandbox',
		extract: 'Hello, I am the non-free image and parenthetical page (YOU CAN\'T SEE THIS). My preview should contain an image that is not free. My preview should contain a parenthetical you cannot see..',
		thumbnail: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/2/2c/RH-Fedora_logo-nonfree.png',
			width: 300,
			height: 126,
			original: 'https://upload.wikimedia.org/wikipedia/commons/2/2c/RH-Fedora_logo-nonfree.png'
		},
		originalimage: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/2/2c/RH-Fedora_logo-nonfree.png',
			width: 300,
			height: 126
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-02-17T22:29:56Z'
	},
	// As above, a no "px-" thumbnail is provided which is not customizable.
	RESTBASE_RESPONSE_WITH_NO_PX_IMAGE = {
		type: 'standard',
		title: 'Barack Obama',
		extract: 'Barack Hussein Obama II born August 4, 1961) ...',
		thumbnail: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg',
			width: 800,
			height: 1000
		},
		originalimage: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg',
			width: 800,
			height: 1000
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-01-30T10:17:41Z',
		description: '44th President of the United States of America'
	},
	RESTBASE_RESPONSE_NONIMAGE_ORIGINAL = {
		type: 'standard',
		title: 'Completing the square',
		extract: 'Landscape',
		thumbnail: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Completing_the_square.ogv/320px--Completing_the_square.ogv.jpg',
			width: 320,
			height: 240
		},
		originalimage: {
			source: 'https://upload.wikimedia.org/wikipedia/commons/3/3d/Completing_the_square.ogv',
			width: 640,
			height: 480
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2018-04-14T05:31:14Z',
		description: 'technique used to solve a quadratic equation.'
	},
	RESTBASE_RESPONSE_WITH_LANDSCAPE_IMAGE = {
		type: 'standard',
		title: 'Landscape',
		extract: 'Landscape',
		thumbnail: {
			source: 'http://foo/bar/baz.png/500px-baz.png',
			width: 500,
			height: 300,
			original: 'http://foo/bar/baz.png'
		},
		originalimage: {
			source: 'http://foo/bar/baz.png',
			width: 1000,
			height: 600
		},
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-02-17T22:29:56Z'
	},
	RESTBASE_RESPONSE_DISAMBIGUATION = {
		type: 'disambiguation',
		title: 'Barack (disambiguation)',
		extract: 'Barack Hussein Obama II born August 4, 1961) ...',
		lang: 'en',
		dir: 'ltr',
		timestamp: '2017-02-17T22:29:56Z'
	},
	RESTBASE_RESPONSE_PREVIEW_MODEL = createModel(
		'Barack Obama',
		'url/Barack Obama', // Generated in the stub below
		'en',
		'ltr',
		'!Barack Hussein Obama II born August 4, 1961) ...!',
		'standard',
		{
			source: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.jpg/409px-President_Barack_Obama.jpg',
			width: 409,
			height: 512
		}
	),
	RESTBASE_RESPONSE_DISAMBIGUATION_MODEL = createModel(
		'Barack (disambiguation)',
		'url/Barack (disambiguation)',
		'en',
		'ltr',
		'!Barack Hussein Obama II born August 4, 1961) ...!',
		'disambiguation'
	);

function provideParsedExtract( page ) {
	return `!${ page.extract }!`;
}

QUnit.module( 'gateway/rest', {
	beforeEach() {
		mw.Title = function ( title ) {
			this.getUrl = () => `url/${ title }`;
		};
	},
	afterEach() {
		mw.Title = null;
	}
} );

QUnit.test( 'RESTBase gateway is called with correct arguments', function ( assert ) {
	const config = Object.assign( {}, DEFAULT_CONSTANTS, {
		acceptLanguage: 'pl'
	} );

	const getSpy = this.sandbox.spy(),
		gateway = createRESTBaseGateway( getSpy, config ),
		expectedOptions = {
			url: DEFAULT_CONSTANTS.endpoint + encodeURIComponent( 'Test Title' ),
			headers: {
				Accept: 'application/json; charset=utf-8; ' +
					'profile="https://www.mediawiki.org/wiki/Specs/Summary/1.2.0"',
				'Accept-Language': 'pl'
			}
		};

	gateway.fetch( 'Test Title' );
	assert.deepEqual( getSpy.getCall( 0 ).args[ 0 ], expectedOptions, 'options' );
} );

QUnit.test( 'RESTBase provider uses extract parser', function ( assert ) {
	const getSpy = this.sandbox.spy(),
		gateway = createRESTBaseGateway();

	gateway.convertPageToModel( RESTBASE_RESPONSE, 512, getSpy );
	assert.deepEqual(
		getSpy.getCall( 0 ).args[ 0 ],
		RESTBASE_RESPONSE,
		'The gateway parser was called with the correct arguments.'
	);
} );

QUnit.test( 'RESTBase gateway is correctly converting the page data to a model', ( assert ) => {
	const gateway = createRESTBaseGateway();

	assert.deepEqual(
		gateway.convertPageToModel( RESTBASE_RESPONSE, 512, provideParsedExtract ),
		RESTBASE_RESPONSE_PREVIEW_MODEL,
		'The gateway unmarshals responses.'
	);
} );

QUnit.test( 'RESTBase gateway is correctly converting the page data to a disambiguation model', ( assert ) => {
	const gateway = createRESTBaseGateway();

	assert.deepEqual(
		gateway.convertPageToModel( RESTBASE_RESPONSE_DISAMBIGUATION,
			512, provideParsedExtract ),
		RESTBASE_RESPONSE_DISAMBIGUATION_MODEL,
		'The gateway unmarshals disambiguations.'
	);
} );

QUnit.test( 'RESTBase gateway doesn\'t stretch thumbnails', ( assert ) => {
	const gateway = createRESTBaseGateway();

	let model = gateway.convertPageToModel(
		RESTBASE_RESPONSE, 2000, provideParsedExtract );

	assert.deepEqual(
		model.thumbnail,
		RESTBASE_RESPONSE.originalimage,
		'If the requested thumbnail size is bigger than that of the original, then use the original.'
	);

	// ---
	model = gateway.convertPageToModel(
		RESTBASE_RESPONSE,
		RESTBASE_RESPONSE.originalimage.height,
		provideParsedExtract
	);

	assert.deepEqual(
		model.thumbnail,
		RESTBASE_RESPONSE.originalimage,
		'If the requested thumbnail size is the same as that of the original, then use the original.'
	);

	// ---
	model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_WITH_SMALL_IMAGE, 320, provideParsedExtract );

	assert.deepEqual(
		model.thumbnail,
		RESTBASE_RESPONSE_WITH_SMALL_IMAGE.originalimage,
		'If the requested thumbnail can\'t be generated because the original is too small, then use the original.'
	);

	// ---
	model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_WITH_LANDSCAPE_IMAGE, 640, provideParsedExtract );

	assert.deepEqual(
		model.thumbnail,
		{
			source: 'http://foo/bar/baz.png/640px-baz.png',
			width: 640,
			height: 384 // ( 640 / 500 ) * 300
		},
		'When the requested thumbnail is scaled, then its largest dimension is preserved.'
	);
} );

QUnit.test( 'RESTBase gateway handles thumbnail URLs with missing dimensions', ( assert ) => {
	const gateway = createRESTBaseGateway();
	const model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_WITH_NO_PX_IMAGE, 300, provideParsedExtract );

	assert.strictEqual(
		model.thumbnail.source,
		'https://upload.wikimedia.org/wikipedia/commons/8/8d/President_Barack_Obama.jpg',
		'If restbase handles missing image dimensions in thumbnail URLs'
	);
} );

QUnit.test( 'RESTBase gateway handles awkward thumbnails', ( assert ) => {
	const gateway = createRESTBaseGateway();

	const response = Object.assign( {}, RESTBASE_RESPONSE );
	response.thumbnail = Object.assign( {}, RESTBASE_RESPONSE.thumbnail );
	response.thumbnail.source = 'http://foo.bar/baz/Qux-320px-Quux.png/800px-Qux-320px-Quux.png';

	const model =
		gateway.convertPageToModel( response, 500, provideParsedExtract );

	assert.deepEqual(
		model.thumbnail.source,
		'http://foo.bar/baz/Qux-320px-Quux.png/400px-Qux-320px-Quux.png',
		'If the requested thumbnail size is the same as that of the original, then use the original.'
	);
} );

QUnit.test( 'RESTBase gateway always scales SVGs', ( assert ) => {
	const gateway = createRESTBaseGateway();

	const model = gateway.convertPageToModel(
		SVG_RESTBASE_RESPONSE, 2000, provideParsedExtract );

	assert.strictEqual(
		model.thumbnail.source,
		'https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/President_Barack_Obama.svg/1600px-President_Barack_Obama.svg.png',
		'If the requested thumbnail is for an SVG, then it\'s always scaled.'
	);
} );

QUnit.test( 'RESTBase gateway only returns safe image formats', ( assert ) => {
	const gateway = createRESTBaseGateway();

	let model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_NONIMAGE_ORIGINAL, 640, provideParsedExtract );

	assert.strictEqual(
		model.thumbnail,
		undefined,
		`If the original image is a video file, and the requested thumbnail
		is larger than the original video file, no thumbnail is returned.`
	);

	model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_NONIMAGE_ORIGINAL, 320, provideParsedExtract );

	assert.strictEqual(
		model.thumbnail.source,
		RESTBASE_RESPONSE_NONIMAGE_ORIGINAL.thumbnail.source,
		`If the original image is a video file, and the requested thumbnail
		is smaller than the original video file, the thumbnail is returned.`
	);
} );

QUnit.test( 'RESTBase gateway handles API failure', function ( assert ) {
	const api = this.sandbox.stub()
			.returns( $.Deferred().reject( { status: 500 } ).promise() ),
		gateway = createRESTBaseGateway( api, {} );

	return gateway.fetchPreviewForTitle( createStubTitle( 1, 'Test Title' ) ).catch( () => {
		assert.true( true, 'The gateway threw an error.' );
	} );
} );

QUnit.test( 'RESTBase gateway handles 404 as a failure', function ( assert ) {
	const api = this.sandbox.stub()
			.returns( $.Deferred().reject( { status: 404 } ).promise() ),
		gateway = createRESTBaseGateway( api, {} );

	return gateway.fetchPreviewForTitle( createStubTitle( 1, 'Test Title' ) ).catch( () => {
		assert.true( true, 'The gateway threw an error.' );
	} );
} );

QUnit.test( 'RESTBase gateway returns the correct data ', function ( assert ) {
	const api = this.sandbox.stub().returns(
			$.Deferred().resolve( RESTBASE_RESPONSE ).promise()
		),
		gateway = createRESTBaseGateway(
			api, DEFAULT_CONSTANTS, provideParsedExtract );

	return gateway.fetchPreviewForTitle( createStubTitle( 1, 'Test Title' ) ).then( ( result ) => {
		assert.deepEqual(
			result,
			RESTBASE_RESPONSE_PREVIEW_MODEL,
			'The gateway returned a response.'
		);
	} );
} );

QUnit.test( 'RESTBase gateway handles missing images ', ( assert ) => {
	const gateway = createRESTBaseGateway();
	const model = gateway.convertPageToModel(
		RESTBASE_RESPONSE_WITHOUT_IMAGE, 300, provideParsedExtract );

	assert.strictEqual(
		model.originalimage,
		undefined,
		'If restbase handles missing image information'
	);
} );

QUnit.test( 'RESTBase gateway handles missing extracts', function ( assert ) {
	const
		api = this.sandbox.stub().returns( $.Deferred().resolve( {} ).promise() ),
		gateway = createRESTBaseGateway(
			api, DEFAULT_CONSTANTS, provideParsedExtract );

	return gateway.fetchPreviewForTitle( createStubTitle( 0, 'Test Title with missing extract' ) )
		.then( ( result ) => {
			assert.strictEqual( result.title, 'Test Title with missing extract', 'Title' );
			assert.strictEqual( result.extract, '!!', 'Extract' );
		} );
} );

QUnit.test( 'RESTBase gateway handles no content success responses', function ( assert ) {
	const api = this.sandbox.stub()
			.returns( $.Deferred().resolve( { status: 204 } ).promise() ),
		gateway = createRESTBaseGateway(
			api, DEFAULT_CONSTANTS, provideParsedExtract );

	return gateway.fetchPreviewForTitle( createStubTitle( 0, 'Test Title with empty response' ) )
		.then( ( result ) => {
			assert.strictEqual( result.title, 'Test Title with empty response', 'Title' );
			assert.strictEqual( result.extract, '!!', 'Extract' );
		} );
} );

QUnit.test( 'RESTBase gateway is abortable', function ( assert ) {
	const
		deferred = $.Deferred(),
		api = this.sandbox.stub().returns(
			deferred.promise( {
				abort() {
					deferred.reject( 'http' );
				}
			} )
		),
		gateway = createRESTBaseGateway(
			api, DEFAULT_CONSTANTS, provideParsedExtract );

	const xhr = gateway.fetchPreviewForTitle( createStubTitle( 1, 'Test Title' ) );

	const chain = xhr.then( () => {
		assert.true( false, 'It never calls a thenable after rejection' );
	} ).catch( ( data ) => {
		assert.strictEqual( data, 'http' );
	} );

	xhr.abort();

	return chain;
} );
