import {Vetools} from "./Vetools.js";
import {LGT} from "./Util.js";
import {UtilActors} from "./UtilActors.js";
import {DataConverter} from "./DataConverter.js";
import {Config} from "./Config.js";
import {UtilList2} from "./UtilList2.js";
import {UtilApplications} from "./UtilApplications.js";
import {ImportListCharacter} from "./ImportListCharacter.js";
import {DataConverterBackground} from "./DataConverterBackground.js";
import {Charactermancer_StartingEquipment} from "./UtilCharactermancerEquipment.js";
import {
	Charactermancer_Background_Characteristics,
	Charactermancer_Background_Features,
} from "./UtilCharactermancerBackground.js";
import {UtilDataSource} from "./UtilDataSource.js";
import {ModalFilterBackgroundsFvtt} from "./UtilModalFilter.js";

class ImportListBackground extends ImportListCharacter {
	// region External
	static init () {
		this._initCreateSheetItemHook({
			prop: "background",
			importerName: "Background",
		});
	}
	// endregion

	constructor (externalData) {
		externalData = externalData || {};
		super(
			{title: "Import Backgrounds"},
			externalData,
			{
				props: ["background"],
				titleSearch: "backgrounds",
				sidebarTab: "items",
				gameProp: "items",
				defaultFolderPath: ["Backgrounds"],
				folderType: "Item",
				pageFilter: new PageFilterBackgrounds(),
				isActorRadio: true,
				page: UrlUtil.PG_BACKGROUNDS,
				isPreviewable: true,
				isDedupable: true,
			},
		);

		this._modalFilterBackgrounds = null;
	}

	async pGetSources () {
		return [
			new UtilDataSource.DataSourceUrl(
				Config.get("ui", "isStreamerMode") ? "SRD" : "5etools",
				Vetools.DATA_URL_BACKGROUNDS,
				{
					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],
				},
			),
			...(await Vetools.pGetHomebrewSources("background")).map(({name, url}) => new UtilDataSource.DataSourceUrl(
				name,
				url,
				{
					filterTypes: [UtilDataSource.SOURCE_TYP_BREW],
				},
			)),
		]
	}

	getData () {
		const out = {
			isPreviewable: this._isPreviewable,
			titleButtonRun: this._titleButtonRun,
			titleSearch: this._titleSearch,
			cols: [
				{
					name: "Name",
					width: 4,
					field: "name",
				},
				{
					name: "Skills",
					width: 6,
					field: "skills",
				},
				{
					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);

				return {
					name: it.name,
					skills: it._skillDisplay,
					source: it.source,
					sourceShort: Parser.sourceJsonToAbv(it.source),
					sourceLong: Parser.sourceJsonToFull(it.source),
					sourceClassName: Parser.sourceJsonToColor(it.source),
					ix,
				};
			}),
		};

		if (this._actor) {
			out.isRadio = true;
		}

		return out;
	}

	_activateListeners_absorbListItems () {
		this._list.doAbsorbItems(
			this._content,
			{
				fnGetName: it => it.name,
				// values used for sorting/search
				fnGetValues: it => ({
					source: it.source,
					skills: it._skillDisplay,
					normalisedTime: it._normalisedTime,
					normalisedRange: it._normalisedRange,
					hash: UrlUtil.URL_TO_HASH_BUILDER[this._page](it),
				}),
				fnGetData: UtilList2.absorbFnGetData,
				fnBindListeners: it => this._actor
					? UtilList2.absorbFnBindListenersRadio(this._list, it)
					: UtilList2.absorbFnBindListeners(this._list, it),
			},
		);
	}

	async pInit () {
		await super.pInit();

		// Create a modal background filter such that we can handle swapping in features from other backgrounds
		this._modalFilterBackgrounds = new ModalFilterBackgroundsFvtt({
			namespace: "ImportListBackground.customFeatures",
			isRadio: true,
			allData: this._content,
		});
		await this._modalFilterBackgrounds.pPreloadHidden();
	}

	/**
	 * @param bg
	 * @param importOpts Options object.
	 * @param [importOpts.isTemp] if the item should be temporary, and displayed.
	 * @param [importOpts.filterValues] Saved filter values to be used instead of our own.
	 */
	async pImportEntry (bg, importOpts) {
		importOpts = importOpts || {};

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

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

	async _pImportEntry_pImportToActor (bg, importOpts) {
		// Build actor update
		const actUpdate = {
			data: {
				details: {
					background: bg.name,
				},
			},
		};

		const dataBuilderOpts = new ImportListBackground.ImportEntryOpts();

		await this._pImportEntry_pFillProficiencies(bg, actUpdate.data, dataBuilderOpts);
		if (dataBuilderOpts.isCancelled) return {status: UtilApplications.TASK_EXIT_CANCELLED};
		await this._pImportEntry_pFillDetails(bg, actUpdate, dataBuilderOpts);
		if (dataBuilderOpts.isCancelled) return {status: UtilApplications.TASK_EXIT_CANCELLED};
		await this._pImportEntry_pFillItems(bg, actUpdate, dataBuilderOpts);

		// Handle starting equipment, if present
		const formDataEquipment = await this._pImportEntry_pImportToActor_pImportStartingEquipment(bg, dataBuilderOpts);
		if (dataBuilderOpts.isCancelled) return {status: UtilApplications.TASK_EXIT_CANCELLED};

		// Copy over equipment's actor currency update, as required
		if (formDataEquipment?.data?.currency) MiscUtil.set(actUpdate, "data", "currency", formDataEquipment.data.currency);

		this._actor.update(actUpdate);

		// Import equipment
		await Charactermancer_StartingEquipment.pImportEquipmentItemEntries(this._actor, formDataEquipment);

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

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

	async _pImportEntry_pImportToActor_pImportStartingEquipment (bg, opts) {
		if (!bg.startingEquipment) return;

		const clsStyleData = {
			defaultData: bg.startingEquipment,
		};
		const startingEquipment = new Charactermancer_StartingEquipment({
			actor: this._actor,
			startingEquipment: clsStyleData,
			appSubTitle: bg.name,
			equiSpecialSource: bg.source,
			equiSpecialPage: bg.page,
		});
		const formData = await startingEquipment.pWaitForUserInput();
		if (formData == null) {
			opts.isCancelled = true;
			return null;
		}
		return formData;
	}

	_pImportEntry_pImportToDirectoryGeneric_pGetImportableData (it, getItemOpts) {
		return DataConverterBackground.getBackgroundItem(it, getItemOpts);
	}

	async _pImportEntry_pFillProficiencies (bg, data, dataBuilderOpts) {
		const isCustomizeSkills = !bg._foundryIsSkipCustomizeSkills && await InputUiUtil.pGetUserBoolean({
			title: `Customize Background: Skills`,
			htmlDescription: `<div class="w-640p">${Renderer.get().render(`Would you like to {@book customize your skill selection|phb|4|backgrounds|customizing a background}?<br>This allows you to choose any two skills to gain from your background, rather than gaining the defaults.`)}</div>`,
			textNo: "Use Default",
			textYes: "Customize",
		});
		dataBuilderOpts.isCustomize = dataBuilderOpts.isCustomize || !!isCustomizeSkills;

		if (isCustomizeSkills) {
			await DataConverter.pFillActorSkillData(
				MiscUtil.get(this._actor, "data", "data", "skills"),
				UtilActors.BG_SKILL_PROFS_CUSTOMIZE,
				data,
				dataBuilderOpts,
			);
		} else {
			await DataConverter.pFillActorSkillData(
				MiscUtil.get(this._actor, "data", "data", "skills"),
				bg.skillProficiencies,
				data,
				dataBuilderOpts,
			);
		}
		if (dataBuilderOpts.isCancelled) return;

		const isCustomizeLangsTools = !bg._foundryIsSkipCustomizeLanguagesTools && await InputUiUtil.pGetUserBoolean({
			title: `Customize Background: Languages & Tools`,
			htmlDescription: `<div class="w-640p">${Renderer.get().render(`Would you like to {@book customize your language and tool selection|phb|4|backgrounds|customizing a background}?<br>This allows you to choose a total of any two languages and/or tool proficiencies to gain from your background, rather than gaining the defaults.`)}</div>`,
			textNo: "Use Default",
			textYes: "Customize",
		});
		dataBuilderOpts.isCustomize = dataBuilderOpts.isCustomize || !!isCustomizeLangsTools;

		if (isCustomizeLangsTools) {
			await DataConverter.pFillActorLanguageOrToolData(
				MiscUtil.get(this._actor, "data", "data", "traits", "languages"),
				MiscUtil.get(this._actor, "data", "data", "traits", "toolProf"),
				UtilActors.LANG_TOOL_PROFS_CUSTOMIZE,
				data,
				dataBuilderOpts,
			);
		} else {
			await DataConverter.pFillActorLanguageData(
				MiscUtil.get(this._actor, "data", "data", "traits", "languages"),
				bg.languageProficiencies,
				data,
				dataBuilderOpts,
			);
			if (dataBuilderOpts.isCancelled) return;

			dataBuilderOpts.isCancelled = await DataConverter.pFillActorToolProfData(
				MiscUtil.get(this._actor, "data", "data", "traits", "toolProf"),
				bg.toolProficiencies,
				data,
			);
		}
	}

	async _pImportEntry_pFillDetails (bg, actUpdate, opts) {
		if (bg._foundryIsSkipImportCharacteristics) return;
		if (bg._foundryFormDataCharacteristics) return Charactermancer_Background_Characteristics.applyFormDataToActorUpdate(actUpdate, bg._foundryFormDataCharacteristics);
		await Charactermancer_Background_Characteristics.pFillActorCharacteristicsData(bg.entries, actUpdate, opts);
	}

	async _pImportEntry_pFillItems (bg, data, dataBuilderOpts) {
		const formDataFeatures = bg._foundryFormDataFeatures ?? await Charactermancer_Background_Features.pGetUserInput({entries: bg.entries, modalFilter: this._modalFilterBackgrounds});
		if (!formDataFeatures) return dataBuilderOpts.isCancelled = true;
		if (formDataFeatures !== VeCt.SYM_UI_SKIP) {
			dataBuilderOpts.isCustomize = dataBuilderOpts.isCustomize || formDataFeatures.data?.isCustomize;

			for (const entry of formDataFeatures?.data?.entries) {
				dataBuilderOpts.items.push(await DataConverterBackground.pGetBackgroundFeatureItem(bg, entry, this._actor, dataBuilderOpts));
			}
		}

		if (dataBuilderOpts.items.length) await UtilActors.pAddActorItems(this._actor, dataBuilderOpts.items);
	}
}

ImportListBackground.ImportEntryOpts = class extends ImportListCharacter.ImportEntryOpts {
	constructor (opts) {
		opts = opts || {};
		super(opts);

		this.isCustomize = false;
	}
}

export {ImportListBackground};
