import {
    createLocation,
    generateLocation,
    getLocationGeneratorOptions,
    getLocationGenerators,
    openLocation
} from "../../../api/locations.js";
import { LocationParentReference } from "../models/LocationParentReference.js";
import { LocationProfileViewModel } from "../models/LocationProfileViewModel.js";
import { PlaceParentService } from "../../places/services/PlaceParentService.js";
import { LocationDataEditor } from "./LocationDataEditor.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;

export class LocationCreateDialog extends HandlebarsApplicationMixin(ApplicationV2) {
    #parentKey = null;
    #selectedGeneratorId = null;
    #selectedTypeId = "location";
    #candidate = null;
    #onCreated = null;
    #openAfterCreate = true;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-location-create",
        classes: ["augur-nexus", "npc-create-dialog", "location-create-dialog"],
        tag: "div",
        window: { title: "New Location", resizable: false, minimizable: false },
        position: { width: 440, height: "auto" },
        actions: {
            acceptCandidate: LocationCreateDialog._onAcceptCandidate,
            cancel: LocationCreateDialog._onCancel,
            rerollCandidate: LocationCreateDialog._onRerollCandidate,
            editCandidate: LocationCreateDialog._onEditCandidate
        }
    };

    static PARTS = {
        content: {
            template: "modules/augur-nexus/templates/campaign-entities/location-create-dialog.hbs",
            templates: [
                "modules/augur-nexus/templates/campaign-entities/location-profile-panel.hbs"
            ]
        }
    };

    static show({ parent = null, parentKey = null, onCreated = null, openAfterCreate = true } = {}) {
        if (!game.user.isGM) return null;
        const app = new this({ parent, parentKey, onCreated, openAfterCreate });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ parent = null, parentKey = null, onCreated = null, openAfterCreate = true } = {}, options = {}) {
        super(options);
        this.#parentKey = String(parentKey || LocationParentReference.toNodeKey(parent) || "").trim() || null;
        this.#onCreated = typeof onCreated === "function" ? onCreated : null;
        this.#openAfterCreate = openAfterCreate !== false;
    }

    async _prepareContext() {
        const generators = getLocationGenerators();
        if (!generators.some(generator => generator.id === this.#selectedGeneratorId)) {
            this.#selectedGeneratorId = generators[0]?.id || "";
        }
        const options = this.#selectedGeneratorId
            ? await getLocationGeneratorOptions(this.#selectedGeneratorId)
            : { types: [], defaults: {} };
        if (!options.types.some(type => type.id === this.#selectedTypeId)) {
            this.#selectedTypeId = options.defaults?.typeId || options.types[0]?.id || "location";
        }
        const parentOptions = PlaceParentService.getParentOptions({
            childType: "location",
            selectedKey: this.#parentKey
        });
        return {
            generators: generators.map(generator => ({
                ...generator,
                selected: generator.id === this.#selectedGeneratorId
            })),
            types: options.types.map(type => ({
                ...type,
                selected: type.id === this.#selectedTypeId
            })),
            hasTypes: options.types.length > 0,
            candidate: this.#candidate ? {
                profile: LocationProfileViewModel.fromCandidate(this.#candidate, {
                    emptyLabel: "No location details generated."
                }),
                data: this.#candidate
            } : null,
            hasCandidate: !!this.#candidate,
            parentOptions,
            isRootSelected: !this.#parentKey,
            parentKey: this.#parentKey || "",
        };
    }

    _onRender(context, options) {
        super._onRender(context, options);
        const form = this.element?.querySelector("form");
        form?.addEventListener("submit", event => {
            event.preventDefault();
            this.#generateCandidate(form);
        });
        this.element?.querySelector("[name='generatorId']")?.addEventListener("change", event => {
            this.#selectedGeneratorId = event.currentTarget.value || "";
            this.#selectedTypeId = "";
            this.#candidate = null;
            this.render({ parts: ["content"] });
        });
        this.element?.querySelector("[name='typeId']")?.addEventListener("change", async event => {
            this.#selectedTypeId = event.currentTarget.value || "location";
            if (this.#candidate) await this.#generateCandidate(form);
        });
        this.element?.querySelector("[name='parentKey']")?.addEventListener("change", event => {
            this.#parentKey = String(event.currentTarget.value || "").trim() || null;
            if (this.#candidate) {
                this.#candidate = foundry.utils.mergeObject(this.#candidate, {
                    hierarchy: { parent: LocationParentReference.fromNodeKey(this.#parentKey) }
                }, { inplace: false, recursive: true });
            }
        });
    }

    static async _onCancel() {
        await this.close();
    }

    static async _onRerollCandidate() {
        const form = this.element?.querySelector("form") || this.element;
        await this.#generateCandidate(form);
    }

    static async _onEditCandidate() {
        if (!this.#candidate) return;
        LocationDataEditor.show({
            candidate: this.#candidate,
            onSave: updated => {
                this.#candidate = updated;
                this.#parentKey = LocationParentReference.toNodeKey(updated?.hierarchy?.parent);
                this.render({ parts: ["content"] });
            }
        });
    }

    static async _onAcceptCandidate() {
        if (!this.#candidate) return;
        try {
            const location = await createLocation(this.#candidate);
            await this.#onCreated?.(location, { candidate: this.#candidate });
            await this.close();
            if (this.#openAfterCreate) await openLocation(location);
        } catch (error) {
            console.error(error);
            ui.notifications.error(error?.message || "Failed to create location.");
        }
    }

    async #generateCandidate(form) {
        if (!form) return;
        const data = new FormData(form);
        const generatorId = String(data.get("generatorId") || this.#selectedGeneratorId || "").trim();
        const typeId = String(data.get("typeId") || this.#selectedTypeId || "location").trim();
        if (!generatorId) return;
        this.#selectedGeneratorId = generatorId;
        this.#selectedTypeId = typeId;
        this.#parentKey = String(data.get("parentKey") || "").trim() || null;

        try {
            const generated = await generateLocation(generatorId, { typeId });
            this.#candidate = foundry.utils.mergeObject(generated, {
                hierarchy: { parent: LocationParentReference.fromNodeKey(this.#parentKey) }
            }, { inplace: false, recursive: true });
            this.render({ parts: ["content"] });
        } catch (error) {
            console.error(error);
            ui.notifications.error(error?.message || "Failed to generate location candidate.");
        }
    }
}
