import {ImportList} from "./ImportList.js";
import {Vetools} from "./Vetools.js";
import {LGT, Util} from "./Util.js";
import {UtilActors} from "./UtilActors.js";
import {Config} from "./Config.js";
import {UtilList2} from "./UtilList2.js";
import {DataConverterSpell} from "./DataConverterSpell.js";
import {SharedConsts} from "../shared/SharedConsts.js";
import {UtilApplications} from "./UtilApplications.js";
import {UtilDataSource} from "./UtilDataSource.js";

class ImportListSpell extends ImportList {
	constructor (externalData) {
		externalData = externalData || {};
		super(
			{title: "Import Spells"},
			externalData,
			{
				props: ["spell"],
				titleSearch: "spells",
				sidebarTab: "items",
				gameProp: "items",
				defaultFolderPath: ["Spells"],
				folderType: "Item",
				fnListSort: PageFilterSpells.sortSpells,
				pageFilter: new PageFilterSpells(),
				page: UrlUtil.PG_SPELLS,
				isPreviewable: true,
				isModdable: true,
				isDedupable: true,
			},
		);
	}

	async pGetSources () {
		const spellIndex = await Vetools.pGetSpellIndex();

		return [
			new UtilDataSource.DataSourceSpecial(
				Config.get("ui", "isStreamerMode") ? "SRD" : "5etools",
				async () => (await Vetools.pGetAllSpells()).spell,
				{
					cacheKey: `5etools-spells`,
					filterTypes: [UtilDataSource.SOURCE_TYP_OFFICIAL_ALL],
					isDefault: true,
				},
			),
			new UtilDataSource.DataSourceUrl(
				"Custom URL",
				"",
				{
					filterTypes: [UtilDataSource.SOURCE_TYP_CUSTOM],
				},
			),
			new UtilDataSource.DataSourceFile(
				"Upload File",
				{
					filterTypes: [UtilDataSource.SOURCE_TYP_CUSTOM],
				},
			),
			...Object.entries(spellIndex).map(([src, filename]) => new UtilDataSource.DataSourceUrl(
				Parser.sourceJsonToFull(src),
				Vetools.getSpellUrl(filename),
				{
					source: src,
					filterTypes: SourceUtil.isNonstandardSource(src) ? [UtilDataSource.SOURCE_TYP_ARCANA] : [UtilDataSource.SOURCE_TYP_OFFICIAL_SINGLE],
				},
			)),
			...(await Vetools.pGetHomebrewSources("spell")).map(({name, url}) => new UtilDataSource.DataSourceUrl(
				name,
				url,
				{
					filterTypes: [UtilDataSource.SOURCE_TYP_BREW],
				},
			)),
		]
	}

	getFolderPathMeta () {
		return {
			...super.getFolderPathMeta(),
			level: {
				label: "Level",
				getter: it => `${Parser.spLevelToFull(it.level)}${it.level ? " level" : ""}`,
			},
			spellPoints: {
				label: "Spell Points",
				getter: it => {
					const sp = (() => {
						switch (it.level) {
							case 1: return 2;
							case 2: return 3;
							case 3: return 5;
							case 4: return 6;
							case 5: return 7;
							case 6: return 8;
							case 7: return 10;
							case 8: return 11;
							case 9: return 13;
							case 0:
							default: return 0;
						}
					})();
					return `${sp} Spell Points`
				},
			},
		}
	}

	getData () {
		return {
			isPreviewable: this._isPreviewable,
			titleButtonRun: this._titleButtonRun,
			titleSearch: this._titleSearch,
			buttonsAdditional: [
				{
					name: "btn-run-mods",
					text: this._actor
						? "Import as Spell Scroll(s)/With Custom Preparation"
						: "Import With Custom Preparation",
				},
			],
			cols: [
				{
					name: "Name",
					width: "3-2",
					field: "name",
				},
				{
					name: "Level",
					width: 1,
					field: "level",
					rowClassName: "text-center",
				},
				{
					name: "Time",
					width: 2,
					field: "time",
					rowClassName: "text-center",
				},
				{
					name: "School",
					width: 1,
					field: "school",
					titleProp: "schoolLong",
					displayProp: "schoolShort",
					classNameProp: "schoolClassName",
					rowClassName: "text-center",
				},
				{
					name: "C.",
					width: "0-3",
					field: "concentration",
					rowClassName: "text-center",
					title: "Concentration",
				},
				{
					name: "Range",
					width: "2-5",
					field: "range",
					rowClassName: "text-right",
				},
				{
					name: "Source",
					width: 1,
					field: "source",
					titleProp: "sourceLong",
					displayProp: "sourceShort",
					classNameProp: "sourceClassName",
					rowClassName: "text-center",
				},
			],
			rows: this._content.map((it, ix) => {
				this._pageFilter.constructor.mutateForFilters(it);
				it._l_time = PageFilterSpells.getTblTimeStr(it.time[0]);
				it._l_school = Parser.spSchoolAbvToFull(it.school, it.subschools);

				return {
					name: it.name,
					level: this.constructor.getListDisplayLevel(it),
					time: it._l_time,
					range: Parser.spRangeToFull(it.range),

					source: it.source,
					sourceShort: Parser.sourceJsonToAbv(it.source),
					sourceLong: Parser.sourceJsonToFull(it.source),
					sourceClassName: Parser.sourceJsonToColor(it.source),

					school: it.school,
					schoolShort: Parser.spSchoolAndSubschoolsAbvsShort(it.school, it.subschools),
					schoolLong: it._l_school,
					schoolClassName: `sp__school-${it.school}`,

					concentration: it._isConc ? "×" : "",
					ix,
				};
			}),
		};
	}

	_activateListeners_absorbListItems () {
		this._list.doAbsorbItems(
			this._content,
			{
				fnGetName: it => it.name,
				// values used for sorting/search
				fnGetValues: it => ({
					source: it.source,
					level: it.level,
					time: it._l_time,
					normalisedTime: it._normalisedTime,
					normalisedRange: it._normalisedRange,
					school: it._l_school,
					concentration: it._isConc,

					hash: UrlUtil.URL_TO_HASH_BUILDER[this._page](it),
				}),
				fnGetData: UtilList2.absorbFnGetData,
				fnBindListeners: it => this._isRadio
					? UtilList2.absorbFnBindListenersRadio(this._list, it)
					: UtilList2.absorbFnBindListeners(this._list, it),
			},
		);
	}

	_pFnPostProcessEntries (entries) {
		if (!this._isUseMods) return entries;

		return new Promise(resolve => {
			const detailer = new ImportListSpell.DetailPreparation(entries, resolve, {titleSearch: this._titleSearch, isActor: !!this._actor});
			detailer.render(true);
		});
	}

	/**
	 * @param spell
	 * @param importOpts Options object.
	 * @param [importOpts.isTemp] if the item should be temporary, and displayed.
	 * @param [importOpts.opts_pGetSpellItem] if the item should be temporary, and displayed.
	 */
	async pImportEntry (spell, importOpts) {
		importOpts = importOpts || {};

		console.log(...LGT, `Importing spell "${spell.name}" (from "${Parser.sourceJsonToAbv(spell.source)}")`);

		if (importOpts.isTemp) return this._pImportEntry_pImportToDirectoryGeneric(spell, importOpts);
		else if (this._actor) return this._pImportEntry_pImportToActor(spell, importOpts);
		else return this._pImportEntry_pImportToDirectoryGeneric(spell, importOpts);
	}

	async _pImportEntry_pImportToActor (spell, importOpts) {
		const spellData = await DataConverterSpell.pGetSpellItem(spell, importOpts.opts_pGetSpellItem || UtilActors.getActorSpellItemOpts(this._actor));

		let embeddedDocument;
		if (spell._foundryIsSpellScroll) {
			const scrollData = await CONFIG.Item.documentClass.createScrollFromSpell(spellData);
			const embeddedItems = await this._actor.createEmbeddedDocuments(
				"Item",
				[scrollData.toObject()],
				{},
			);
			embeddedDocument = embeddedItems[0];
		} else {
			const embeddedItems = await this._actor.createEmbeddedDocuments(
				"Item",
				[spellData],
				{},
			);
			embeddedDocument = embeddedItems[0];
		}

		if (this._actor.isToken) this._actor.sheet.render();

		return {imported: [{name: spell.name, actor: this._actor, embeddedDocument: embeddedDocument}], status: UtilApplications.TASK_EXIT_COMPLETE};
	}

	_pImportEntry_pImportToDirectoryGeneric_pGetImportableData (it, getItemOpts) {
		return DataConverterSpell.pGetSpellItem(it, {...UtilActors.getSpellItemItemOpts(), ...getItemOpts});
	}

	static getListDisplayLevel (it) { return `${Parser.spLevelToFull(it.level)}${it.meta && it.meta.ritual ? " (rit.)" : ""}${it.meta && it.meta.technomagic ? " (tec.)" : ""}`; }
}

// TODO factor this and `ImportListCreature.ScaleRename` out
ImportListSpell.DetailPreparation = class extends Application {
	/**
	 * @param dataList
	 * @param resolve
	 * @param opts Options object.
	 * @param opts.titleSearch Used in prompt text in the search bar.
	 * @param opts.isActor
	 */
	constructor (dataList, resolve, opts) {
		super({
			title: "Set Preparation Details",
			template: `${SharedConsts.MODULE_LOCATION}/template/ImportListSpellScrollOrPreparation.hbs`,
			width: 960,
			height: Util.getMaxWindowHeight(),
			resizable: true,
		});
		this._dataList = dataList;
		this._resolve = resolve;

		this._titleSearch = opts.titleSearch;
		this._isActor = opts.isActor;

		this._list = null;
		this._$btnReset = null;
	}

	getData () {
		return {
			titleSearch: this._titleSearch,
			isActor: this._isActor,
			rows: this._dataList.map((it, ix) => ({
				name: it.name,
				level: ImportListSpell.getListDisplayLevel(it),
				source: it.source,
				sourceShort: Parser.sourceJsonToAbv(it.source),
				sourceLong: Parser.sourceJsonToFull(it.source),
				sourceClassName: Parser.sourceJsonToColor(it.source),
				ix,
			})),
		};
	}

	activateListeners ($html) {
		super.activateListeners($html);

		// Init list library
		this._list = new List({
			$iptSearch: $html.find(`.search`),
			$wrpList: $html.find(`.veapp__list`),
			valueNames: ["name", "source", "level", "ix"],
		});
		const $wrpBtnsSort = $html.find(`[data-name="wrp-btns-sort"]`);
		SortUtil.initBtnSortHandlers($wrpBtnsSort, this._list);
		this._list.doAbsorbItems(
			this._dataList,
			{
				fnGetName: it => it.name,
				fnGetValues: it => ({source: it.source, level: it.level}),
				fnGetData: it => {
					const $e = $(it.ele);
					return {
						$cbScroll: $e.find(`[name="cb-is-scroll"]`),
						$selIsPrepared: $e.find(`[name="sel-is-prepared"]`),
						$selPreparationMode: $e.find(`[name="sel-preparation-mode"]`),
					};
				},
			},
		);
		this._list.init();

		const $cbScrollAll = $wrpBtnsSort.find(`[name="cb-is-scroll-all"]`)
			.change(() => {
				const val = $cbScrollAll.prop("checked");
				this._list.items.forEach(li => li.data.$cbScroll.prop("checked", val));
			});

		this._$btnReset = $html.find(`[name="btn-reset"]`).click(() => {
			$html.find(`.search`).val("");
			if (this._list) this._list.reset();
		});

		$html.find(`[name="btn-run"]`).click(async () => {
			const enhancedSpells = this._list.items.map(it => {
				const isSpellScroll = it.data.$cbScroll.prop("checked");
				const isPrepVal = it.data.$selIsPrepared.val();
				const prepModeVal = it.data.$selPreparationMode.val();

				const hasIsPrep = isPrepVal && isPrepVal !== "-1";
				const hasPrepMode = prepModeVal && prepModeVal !== "-1";

				if (!isSpellScroll && !hasIsPrep && !hasPrepMode) return this._dataList[it.ix];

				const out = MiscUtil.copy(this._dataList[it.ix]);

				if (isSpellScroll) out._foundryIsSpellScroll = true;

				if (hasIsPrep || hasPrepMode) {
					out.foundryData = {};
					if (hasIsPrep) out.foundryData["preparation.prepared"] = !!Number(isPrepVal);
					if (hasPrepMode) out.foundryData["preparation.mode"] = prepModeVal;
				}

				return out;
			});

			this._resolve(enhancedSpells);
			this.close();
		});

		// Reset list to initial state
		if (this._$btnReset) this._$btnReset.click();
	}

	async close () {
		this._resolve(null);
		return super.close()
	}
}

export {ImportListSpell};
