"use strict";

jest.mock("./__mocks__/createMappingsSerializer");

const { ConcatSource } = require("../");
const { RawSource } = require("../");
const { OriginalSource } = require("../");
const { SourceMapSource } = require("../");
const { withReadableMappings } = require("./helpers");

describe("concatSource", () => {
	it("should concat two sources", () => {
		const source = new ConcatSource(
			new RawSource("Hello World\n"),
			new OriginalSource(
				"console.log('test');\nconsole.log('test2');\n",
				"console.js",
			),
		);
		source.add(new OriginalSource("Hello2\n", "hello.md"));
		const expectedMap1 = {
			version: 3,
			file: "x",
			mappings: ";AAAA;AACA;ACDA",
			names: [],
			sources: ["console.js", "hello.md"],
			sourcesContent: [
				"console.log('test');\nconsole.log('test2');\n",
				"Hello2\n",
			],
		};
		const expectedSource = [
			"Hello World",
			"console.log('test');",
			"console.log('test2');",
			"Hello2",
			"",
		].join("\n");
		expect(source.size()).toBe(62);
		expect(source.source()).toEqual(expectedSource);
		expect(
			source.map({
				columns: false,
			}),
		).toEqual(expectedMap1);
		expect(
			source.sourceAndMap({
				columns: false,
			}),
		).toEqual({
			source: expectedSource,
			map: expectedMap1,
		});

		const expectedMap2 = {
			version: 3,
			file: "x",
			mappings: ";AAAA;AACA;ACDA",
			names: [],
			sources: ["console.js", "hello.md"],
			sourcesContent: [
				"console.log('test');\nconsole.log('test2');\n",
				"Hello2\n",
			],
		};
		expect(source.map()).toEqual(expectedMap2);
		expect(source.sourceAndMap()).toEqual({
			source: expectedSource,
			map: expectedMap2,
		});
	});

	it("should be able to handle strings for all methods", () => {
		const source = new ConcatSource(
			new RawSource("Hello World\n"),
			new OriginalSource(
				"console.log('test');\nconsole.log('test2');\n",
				"console.js",
			),
		);
		const innerSource = new ConcatSource("(", "'string'", ")");
		innerSource.buffer(); // force optimization
		source.add("console");
		source.add(".");
		source.add("log");
		source.add(innerSource);
		const expectedSource = [
			"Hello World",
			"console.log('test');",
			"console.log('test2');",
			"console.log('string')",
		].join("\n");
		const expectedMap1 = {
			version: 3,
			file: "x",
			mappings: ";AAAA;AACA",
			names: [],
			sources: ["console.js"],
			sourcesContent: ["console.log('test');\nconsole.log('test2');\n"],
		};
		expect(source.size()).toBe(76);
		expect(source.source()).toEqual(expectedSource);
		expect(source.buffer()).toEqual(Buffer.from(expectedSource, "utf8"));
		expect(
			source.map({
				columns: false,
			}),
		).toEqual(expectedMap1);
		expect(
			source.sourceAndMap({
				columns: false,
			}),
		).toEqual({
			source: expectedSource,
			map: expectedMap1,
		});

		const hash = require("crypto").createHash("sha256");

		source.updateHash(hash);
		const digest = hash.digest("hex");
		expect(digest).toBe(
			"183e6e9393eddb8480334aebeebb3366d6cce0124bc429c6e9246cc216167cb2",
		);

		const hash2 = require("crypto").createHash("sha256");

		const source2 = new ConcatSource(
			"Hello World\n",
			new OriginalSource(
				"console.log('test');\nconsole.log('test2');\n",
				"console.js",
			),
			"console.log('string')",
		);
		source2.updateHash(hash2);
		expect(hash2.digest("hex")).toEqual(digest);

		const clone = new ConcatSource();
		clone.addAllSkipOptimizing(source.getChildren());

		expect(clone.source()).toEqual(source.source());

		const hash3 = require("crypto").createHash("sha256");

		clone.updateHash(hash3);
		expect(hash3.digest("hex")).toEqual(digest);
	});

	it("should return null as map when only generated code is concatenated", () => {
		const source = new ConcatSource(
			"Hello World\n",
			new RawSource("Hello World\n"),
			"",
		);

		const resultText = source.source();
		const resultMap = source.sourceAndMap({
			columns: true,
		});
		const resultListMap = source.sourceAndMap({
			columns: false,
		});

		expect(resultText).toBe("Hello World\nHello World\n");
		expect(resultMap.source).toEqual(resultText);
		expect(resultListMap.source).toEqual(resultText);
		expect(resultListMap.map).toBeNull();
		expect(resultMap.map).toBeNull();
	});

	it("should allow to concatenate in a single line", () => {
		const source = new ConcatSource(
			new OriginalSource("Hello", "hello.txt"),
			" ",
			new OriginalSource("World ", "world.txt"),
			"is here\n",
			new OriginalSource("Hello\n", "hello.txt"),
			" \n",
			new OriginalSource("World\n", "world.txt"),
			"is here",
		);

		expect(withReadableMappings(source.map())).toMatchInlineSnapshot(`
		Object {
		  "_mappings": "1:0 -> [hello.txt] 1:0, :5, :6 -> [world.txt] 1:0, :12
		2:0 -> [hello.txt] 1:0
		4:0 -> [world.txt] 1:0",
		  "file": "x",
		  "mappings": "AAAA,K,CCAA,M;ADAA;;ACAA",
		  "names": Array [],
		  "sources": Array [
		    "hello.txt",
		    "world.txt",
		  ],
		  "sourcesContent": Array [
		    "Hello",
		    "World ",
		  ],
		  "version": 3,
		}
	`);
	});

	it("should allow to concat buffer sources", () => {
		const source = new ConcatSource("a", new RawSource(Buffer.from("b")), "c");
		expect(source.sourceAndMap()).toMatchInlineSnapshot(`
		Object {
		  "map": null,
		  "source": "abc",
		}
	`);
	});

	it("should handle column mapping correctly with missing sources", () => {
		const source = new ConcatSource(
			"/*! For license information please see main.js.LICENSE.txt */",
		);
		const innerSource = "ab\nc";
		const innerMap = {
			names: [],
			file: "x",
			version: 3,
			sources: ["main.js"],
			sourcesContent: ["a\nc"],
			mappings: "AAAA,CCAA;ADCA",
			// ______________↑ The column mapping (CCAA) references one missing source
		};
		source.add(new SourceMapSource(innerSource, "main.js", innerMap));
		const expected = {
			source:
				"/*! For license information please see main.js.LICENSE.txt */ab\nc",
			map: {
				version: 3,
				file: "x",
				mappings: "6DAAA,C;AACA",
				sources: ["main.js"],
				sourcesContent: ["a\nc"],
				names: [],
			},
		};
		expect(
			source.sourceAndMap({
				columns: true,
			}),
		).toEqual({
			source: expected.source,
			map: expected.map,
		});
	});
});
