import {ImportListActor} from "./ImportListActor.js";
import {Vetools} from "./Vetools.js";
import {Config} from "./Config.js";
import {DataConverter} from "./DataConverter.js";
import {UtilActors} from "./UtilActors.js";
import {ImportListCreature} from "./ImportListCreature.js";
import {DataConverterVehicle} from "./DataConverterVehicle.js";
import {SharedConsts} from "../shared/SharedConsts.js";
import {UtilDataSource} from "./UtilDataSource.js";
import {ImportListObject} from "./ImportListObject.js";

class ImportListVehicle extends ImportListActor {
	constructor (externalData) {
		externalData = externalData || {};
		super(
			{
				title: "Import Vehicles",
			},
			externalData,
			{
				props: ["vehicle"],
				propsBrewAdditionalData: ["foundryVehicle"],
				fnLoadSideData: Vetools.pGetVehicleSideData,
				titleSearch: "vehicles",
				sidebarTab: "actors",
				gameProp: "actors",
				defaultFolderPath: ["Vehicles"],
				folderType: "Actor",
				pageFilter: new PageFilterVehicles(),
				page: UrlUtil.PG_VEHICLES,
				isDedupable: true,
			},
			{
				actorType: "vehicle",
			},
		);
	}

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

	async _pImportEntry_pGetImportMetadata (actor, veh, importOpts) {
		const act = {};

		const fluff = await Renderer.vehicle.pGetFluff(veh);

		const vehOpts = new ImportListVehicle.ImportEntryOpts({actor, fluff});

		await this._pImportEntry_pFillBase(veh, act, vehOpts.fluff, {isUseTokenImageAsPortrait: Config.get("importVehicle", "isUseTokenImageAsPortrait")});

		act.data = {};

		if (!this._isSkipFolder && !importOpts.isTemp && !this._pack) {
			const folderId = await this._pImportEntry_pGetFolderId(veh);
			if (folderId) act.folder = folderId;
		}

		act.permission = {default: Config.get("importVehicle", "permissions")};

		this._pImportEntry_fillData_Abilities(veh, act.data, vehOpts);
		this._pImportEntry_fillData_Attributes(veh, act.data, vehOpts);
		this._pImportEntry_fillData_Details(veh, act.data, vehOpts);
		this._pImportEntry_fillData_Traits(veh, act.data, vehOpts);
		this._pImportEntry_fillData_Currency(veh, act.data, vehOpts);
		this._pImportEntry_fillData_Cargo(veh, act.data, vehOpts);

		await this._pImportEntry_pFillToken(veh, act, "importVehicle");

		return {dataBuilderOpts: vehOpts, actorData: act};
	}

	/**
	 * If we're importing a creature-type vehicle, delegate to the creature importer.
	 * For object-type vehicles, delegate to the object importer.
	 */
	async pImportEntry (veh, importOpts) {
		importOpts = importOpts || {};

		if (veh.vehicleType === "CREATURE") {
			const importerCreature = new ImportListCreature({});
			await importerCreature.pInit();
			await importerCreature.pSyncStateFrom(this);
			// Override the token URL
			// TODO(future) override fluff, too, if this is ever required
			veh = MiscUtil.copy(veh);
			veh.tokenUrl = Vetools.getTokenUrl("vehicle", veh);
			return importerCreature.pImportEntry(veh, importOpts);
		} else if (veh.vehicleType === "OBJECT") {
			const objectImporter = new ImportListObject({});
			await objectImporter.pInit();
			await objectImporter.pSyncStateFrom(this);
			// Override the token URL
			// TODO(future) override fluff, too, if this is ever required
			veh = MiscUtil.copy(veh);
			veh.tokenUrl = Vetools.getTokenUrl("object", veh);
			return objectImporter.pImportEntry(veh, importOpts);
		}

		return super.pImportEntry(veh, importOpts);
	}

	_pImportEntry_fillData_Attributes (veh, data) {
		const out = {};

		out.init = {
			value: 0,
			bonus: 0,
			mod: 0,
			prof: 0,
			total: 0,
		};

		out.spelldc = null;

		out.movement = DataConverterVehicle.getShipMovement(veh);

		switch (veh.vehicleType) {
			case "INFWAR": {
				const dexMod = Parser.getAbilityModNumber(veh.dex);
				out.ac = {
					value: 19 + dexMod,
					motionless: "19",
				};

				out.hp = {
					value: MiscUtil.get(veh, "hp", "hp") || 0,
					min: 0,
					max: MiscUtil.get(veh, "hp", "hp") || 0,
					temp: 0,
					tempmax: 0,
					dt: MiscUtil.get(veh, "hp", "dt") || 0,
					mt: MiscUtil.get(veh, "hp", "mt") || 0,
				};

				out.actions = {
					stations: true,
					value: 0,
					thresholds: {
						0: 0,
						1: 0,
						2: 0,
					},
				};

				out.capacity = {
					creature: Renderer.vehicle.getInfwarCreatureCapacity(veh),
					cargo: typeof veh.capCargo === "string" ? 0 : veh.capCargo,
				};

				out.speed = `${veh.speed} ft.`;

				break;
			}

			case "SHIP": {
				out.ac = {
					value: MiscUtil.get(veh, "hull", "ac") || 0,
					motionless: "",
				};

				out.hp = {
					value: MiscUtil.get(veh, "hull", "hp") || 0,
					min: 0,
					max: MiscUtil.get(veh, "hull", "hp") || 0,
					temp: 0,
					tempmax: 0,
					dt: MiscUtil.get(veh, "hull", "dt") || 0,
					mt: 0,
				};

				let actionsValue = 0;
				const actionThresholds = {
					0: 0,
					1: 0,
					2: 0,
				};
				if (veh.actionThresholds) {
					Object.entries(veh.actionThresholds)
						.sort(([ka], [kb]) => SortUtil.ascSort(Number(kb), Number(ka)))
						.slice(0, 3) // FVTT supports a max of 3, so take the highest
						.forEach(([actions, crew], i) => {
							actionThresholds[i] = crew;
						});
				}

				out.actions = {
					stations: false,
					value: actionsValue,
					thresholds: actionThresholds,
				};

				out.capacity = {
					creature: Renderer.vehicle.getShipCreatureCapacity(veh),
					cargo: typeof veh.capCargo === "string" ? 0 : veh.capCargo,
				};

				out.speed = `${veh.pace} miles per hour (${veh.pace * 24} miles per day)`;

				break;
			}

			default: throw new Error(`Unhandled vehicle type "${veh.vehicleType}"`);
		}

		data.attributes = out;
	}

	_pImportEntry_fillData_Details (veh, data, vehOpts) {
		const out = {};

		out.biography = {
			value: this._getBiographyValue(veh, vehOpts.fluff, {isImportText: Config.get("importVehicle", "isImportBio"), isImportImages: Config.get("importVehicle", "isImportBioImages")}),
		};

		out.source = DataConverter.getSourceWithPagePart(veh);

		data.details = out;
	}

	_pImportEntry_fillData_Traits (veh, data) {
		const out = {};

		out.size = UtilActors.VET_SIZE_TO_ABV[veh.size] || "med";

		switch (veh.vehicleType) {
			case "INFWAR": {
				out.dimensions = `${veh.weight.toLocaleString()} lb.`;
				break;
			}

			case "SHIP": {
				out.dimensions = veh.dimensions ? veh.dimensions.join(" by ") : "";
				break;
			}

			default: throw new Error(`Unhandled vehicle type "${veh.vehicleType}"`);
		}

		this._pImportEntry_fillConditionsDamage(veh, out);

		data.traits = out;
	}

	_pImportEntry_fillData_Currency (veh, data) {
		// Dummy data
		data.currency = {
			pp: 0,
			gp: 0,
			ep: 0,
			sp: 0,
			cp: 0,
		};
	}

	async _pImportEntry_pFillItems (veh, act, vehOpts, importOpts) {
		await this._pImportEntry_pFillItems_ship(veh, act, vehOpts, importOpts);
		await this._pImportEntry_pFillItems_infWar(veh, act, vehOpts, importOpts);

		const isTemporary = importOpts.isTemp || this._pack != null;
		await UtilActors.pAddActorItems(vehOpts.actor, vehOpts.items, {isTemporary});
	}

	_pImportEntry_pFillItems_ship (veh, act, vehOpts, importOpts) {
		if (veh.control) { // e.g. "Battle Balloon"
			veh.control.forEach(it => this._addShipEquipment(veh, act, vehOpts, importOpts, it, "control"));
		}

		if (veh.movement) { // e.g. "Battle Balloon"
			veh.movement.forEach(it => this._addShipEquipment(veh, act, vehOpts, importOpts, it, "movement"));
		}

		if (veh.weapon) { // e.g. "Battle Balloon"
			veh.weapon.forEach(it => this._addShipWeapon(veh, act, vehOpts, importOpts, it));
		}

		if (veh.other) { // e.g. "Airship" (UAOfShipsAndSea)
			veh.other.forEach(it => this._addShipOther(veh, act, vehOpts, importOpts, it));
		}

		if (veh.action) { // e.g. "Battle Balloon"
			this._addShipAction(veh, act, vehOpts, importOpts, veh.action);
		}
	}

	_addShipEquipment (veh, act, vehOpts, importOpts, equipment, prop) {
		const equipmentItem = DataConverterVehicle.getShipEquipmentItem(veh, equipment, prop);
		if (!equipmentItem) return;
		vehOpts.items.push(equipmentItem);
	}

	_addShipWeapon (veh, act, vehOpts, importOpts, weap) {
		const weaponItem = DataConverterVehicle.getShipWeaponItem(veh, weap);
		if (!weaponItem) return;
		vehOpts.items.push(weaponItem);
	}

	_addShipOther (veh, act, vehOpts, importOpts, ent) {
		const otherItem = DataConverterVehicle.getShipOtherItem(veh, ent);
		if (!otherItem) return;
		vehOpts.items.push(otherItem);
	}

	_addShipAction (veh, act, vehOpts, importOpts, actionEnts) {
		const actionItems = DataConverterVehicle.getShipActionItems(veh, actionEnts);
		if (!actionItems || !actionItems.length) return;
		vehOpts.items.push(...actionItems);
	}

	_pImportEntry_pFillItems_infWar (veh, act, vehOpts, importOpts) {
		if (veh.trait) { // e.g. "Demon Grinder"
			veh.trait.forEach(it => this._addInfWarTrait(veh, act, vehOpts, importOpts, it));
		}

		if (veh.actionStation) { // e.g. "Demon Grinder"
			veh.actionStation.forEach(it => this._addInfWarActionStation(veh, act, vehOpts, importOpts, it));
		}

		if (veh.reaction) { // e.g. "Devil's Ride"
			veh.reaction.forEach(it => this._addInfWarReaction(veh, act, vehOpts, importOpts, it));
		}
	}

	_addInfWarTrait (veh, act, vehOpts, importOpts, trait) {
		const traitItem = DataConverter.getItemActorPassive(
			trait,
			{
				fvttType: "feat",
				mode: "vehicle",
				entity: veh,
				source: veh.source,
				actor: {data: act}, // wrap our update data to give the appearance of a real actor
				img: `modules/${SharedConsts.MODULE_NAME}/media/icon/gears.svg`,
			},
		);
		vehOpts.items.push(traitItem);
	}

	_addInfWarActionStation (veh, act, vehOpts, importOpts, action) {
		const actionStationItem = DataConverterVehicle.getInfWarActionItem(veh, action);
		if (!actionStationItem) return;
		vehOpts.items.push(actionStationItem);
	}

	_addInfWarReaction (veh, act, vehOpts, importOpts, reaction) {
		const reactionItem = DataConverter.getItemActorPassive(
			reaction,
			{
				activationType: "reaction",
				activationCost: 1,
				fvttType: "feat",
				mode: "vehicle",
				entity: veh,
				source: veh.source,
				actor: {data: act}, // wrap our update data to give the appearance of a real actor
				img: `modules/${SharedConsts.MODULE_NAME}/media/icon/gears.svg`,
			},
		);
		vehOpts.items.push(reactionItem);
	}

	_pImportEntry_fillData_Cargo (veh, data) {
		// Dummy data
		data.cargo = {
			crew: [],
			passengers: [],
		};
	}
}

ImportListVehicle.ImportEntryOpts = class extends ImportListActor.ImportEntryOpts {}

export {ImportListVehicle};
