import { getFaction, getFactionGeneratorProfileOptions, getFactionIdeologyTopics, openFactionEmblemEditor, updateFaction } from "../../../api/factions.js";
import { promptTextInput } from "../../../api/ui.js";
import { FactionEntityModel } from "../models/FactionEntityModel.js";
import { FactionEditorFieldRegistry } from "../services/FactionEditorFieldRegistry.js";
import { FactionEmblemPicker } from "./FactionEmblemPicker.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const FIELD_DEFINITIONS = [
    { key: "type", label: "Type" },
    { key: "method", label: "Method" },
    { key: "drive", label: "Drive" },
    { key: "action", label: "Action" },
    { key: "theme", label: "Theme" },
    { key: "focus", label: "Focus" }
];

export class FactionDataEditor extends HandlebarsApplicationMixin(ApplicationV2) {
    #factionId = "";
    #candidate = null;
    #onSave = null;
    #context = {};
    #ideologyTopics = [];
    #positionInitialized = false;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-faction-data-editor",
        classes: ["augur-nexus", "augur-theme-fantasy", "npc-data-editor", "faction-data-editor", "faction-organization-data-editor"],
        tag: "div",
        window: { title: "Edit Organization", resizable: true, minimizable: true },
        position: { width: 1320, height: 720 },
        actions: {
            save: FactionDataEditor._onSave,
            closeEditor: FactionDataEditor._onCloseEditor,
            toggleCombo: FactionDataEditor._onToggleCombo,
            selectComboOption: FactionDataEditor._onSelectComboOption,
            renameFaction: FactionDataEditor._onRenameFaction,
            changeEmblem: FactionDataEditor._onChangeEmblem,
            addIdeologyTopic: FactionDataEditor._onAddIdeologyTopic,
            deleteIdeologyTopic: FactionDataEditor._onDeleteIdeologyTopic,
            addCustomField: FactionDataEditor._onAddCustomField,
            deleteCustomField: FactionDataEditor._onDeleteCustomField,
            toggleDefaultField: FactionDataEditor._onToggleDefaultField,
            moveCustomFieldUp: FactionDataEditor._onMoveCustomFieldUp,
            moveCustomFieldDown: FactionDataEditor._onMoveCustomFieldDown
        }
    };

    static PARTS = { content: { template: "modules/augur-nexus/templates/campaign-entities/faction-data-editor.hbs" } };

    static show({ factionId = "", candidate = null, onSave = null, context = {} } = {}) {
        if (!game.user.isGM || (!factionId && !candidate)) return null;
        const app = new this({ factionId, candidate, onSave, context });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ factionId = "", candidate = null, onSave = null, context = {} } = {}, options = {}) {
        super(options);
        this.#factionId = factionId || "";
        this.#candidate = candidate ? foundry.utils.deepClone(candidate) : null;
        this.#onSave = typeof onSave === "function" ? onSave : null;
        this.#context = foundry.utils.deepClone(context || {});
    }

    async _prepareContext() {
        const entity = this.#getEntity();
        const imageSrc = entity ? await FactionEntityModel.getImageAsync(entity) : "";
        const hiddenDefaultFields = this.#getHiddenDefaultFields(entity);
        const profileOptions = await getFactionGeneratorProfileOptions(entity?.generation?.generatorId || "", {
            templateId: entity?.generation?.templateId || ""
        });
        this.#ideologyTopics = getFactionIdeologyTopics();
        const ideologyRows = FactionEntityModel.getIdeology(entity).map((selection, index) => this.#buildIdeologyRow(selection, index));
        const fieldRows = FIELD_DEFINITIONS.map(definition => {
            const value = String(entity?.profile?.[definition.key] || "");
            const configuredOptions = profileOptions.fields?.[definition.key] || [];
            return { ...definition, value, hidden: hiddenDefaultFields.has(definition.key), options: this.#buildOptions(configuredOptions, value), configured: configuredOptions.length > 0 };
        });
        const typeField = fieldRows.find(field => field.key === "type");
        const fields = fieldRows.filter(field => field.key !== "type");
        return {
            missing: !entity,
            faction: entity ? {
                id: entity.id,
                name: FactionEntityModel.getName(entity),
                subtitle: FactionEntityModel.getSubtitle(entity),
                imageSrc,
                color: FactionEntityModel.getAccent(entity)
            } : { name: "Missing Organization" },
            typeField,
            extensionFields: FactionEditorFieldRegistry.listFor(entity, this.#context),
            fields,
            customFields: FactionEntityModel.getCustomFields(entity),
            ideology: {
                rows: ideologyRows,
                hasRows: ideologyRows.length > 0,
                canAdd: ideologyRows.length < this.#ideologyTopics.length
            }
        };
    }

    _onRender(context, options) {
        super._onRender(context, options);
        if (!this.#positionInitialized) {
            this.setPosition(this.#getCenteredPosition());
            this.#positionInitialized = true;
        }
        this.element.querySelector("form")?.addEventListener("submit", event => {
            event.preventDefault();
            this.#saveForm();
        });
        this.element.addEventListener("click", event => {
            if (!event.target?.closest?.(".npc-data-combo")) this.#closeCombos();
        });
        const colorInput = this.element.querySelector("[data-faction-color-input]");
        colorInput?.addEventListener("input", () => {
            const field = colorInput.closest(".faction-data-color-field");
            if (field) field.style.setProperty("--faction-color", colorInput.value);
            const value = field?.querySelector("[data-faction-color-value]");
            if (value) value.textContent = colorInput.value.toUpperCase();
        });
        for (const row of this.element.querySelectorAll("[data-ideology-row]")) this.#wireIdeologyRow(row);
        for (const select of this.element.querySelectorAll("[data-faction-extension-field]")) {
            select.addEventListener("change", () => {
                const description = select.closest(".faction-editor-extension-control")?.querySelector("[data-faction-extension-description]");
                if (description) description.textContent = select.selectedOptions[0]?.dataset?.description || "";
            });
        }
        this.#refreshIdeologyControls();
    }

    static async _onSave() { return this.#saveForm(); }
    static async _onCloseEditor() { await this.close(); }

    static async _onRenameFaction() {
        const entity = this.#getEntity();
        if (!entity) return;
        const name = await promptTextInput({ title: "Rename Organization", label: "Name", value: FactionEntityModel.getName(entity), confirmLabel: "Rename" });
        if (!name) return;
        if (this.#candidate) {
            this.#candidate.display = { ...(this.#candidate.display || {}), name };
            this.#onSave?.(foundry.utils.deepClone(this.#candidate));
        } else {
            await updateFaction(entity.id, { display: { name } });
            this.#onSave?.(getFaction(this.#factionId));
        }
        this.render({ parts: ["content"] });
    }

    static async _onChangeEmblem() {
        const entity = this.#getEntity();
        if (!entity) return;
        const opened = openFactionEmblemEditor(entity, {
            onSelect: selection => this.#setEmblemSelection(selection)
        });
        if (opened) return;
        new FactionEmblemPicker({ currentEmblemSrc: FactionEntityModel.getImage(entity), onSelect: selection => this.#setEmblem(selection?.src || "") }).render(true, { focus: true });
    }

    static async _onToggleCombo(event, target) {
        const key = target?.dataset?.comboKey || "";
        const combo = key ? this.element.querySelector(`[data-combo="${CSS.escape(key)}"]`) : null;
        if (!combo) return;
        const wasOpen = combo.classList.contains("open");
        this.#closeCombos();
        if (!wasOpen) this.#positionComboMenu(combo);
        combo.classList.toggle("open", !wasOpen);
        combo.querySelector("input")?.focus();
    }

    static async _onSelectComboOption(event, target) {
        const key = target?.dataset?.comboKey || "";
        const combo = key ? this.element.querySelector(`[data-combo="${CSS.escape(key)}"]`) : null;
        const input = combo?.querySelector("input");
        if (!input) return;
        input.value = target?.dataset?.comboValue || "";
        this.#closeCombos();
        input.focus();
    }

    static async _onAddCustomField() {
        const container = this.element.querySelector("[data-custom-fields]");
        if (!container) return;
        const id = `field.${foundry.utils.randomID()}`;
        const row = document.createElement("div");
        row.className = "npc-data-custom-field-row";
        row.dataset.customFieldRow = id;
        row.innerHTML = this.#renderCustomFieldRow({ id, label: "", value: "" });
        this.#wireCustomFieldRow(row);
        container.append(row);
        row.querySelector("[name='customField.label']")?.focus();
    }

    static async _onAddIdeologyTopic() {
        const container = this.element.querySelector("[data-ideology-rows]");
        if (!container) return;
        const used = new Set([...container.querySelectorAll("[data-ideology-topic-select]")].map(select => select.value));
        const topic = this.#ideologyTopics.find(entry => !used.has(entry.id));
        if (!topic) return;
        const row = document.createElement("div");
        row.className = "faction-ideology-editor-row";
        row.dataset.ideologyRow = "";
        row.innerHTML = this.#renderIdeologyRow({ topicId: topic.id, tenetId: topic.tenets[0]?.id || "" });
        container.append(row);
        this.#wireIdeologyRow(row);
        this.#refreshIdeologyControls();
        row.querySelector("[data-ideology-topic-select]")?.focus();
    }

    static async _onDeleteIdeologyTopic(event, target) {
        target?.closest?.("[data-ideology-row]")?.remove();
        this.#refreshIdeologyControls();
    }

    static async _onDeleteCustomField(event, target) { target?.closest?.("[data-custom-field-row]")?.remove(); }
    static async _onToggleDefaultField(event, target) { this.#toggleDefaultFieldButton(target); }
    static async _onMoveCustomFieldUp(event, target) { this.#moveCustomField(target, -1); }
    static async _onMoveCustomFieldDown(event, target) { this.#moveCustomField(target, 1); }

    async #saveForm() {
        const entity = this.#getEntity();
        const form = this.element.querySelector("form");
        if (!entity || !form) return;
        const formData = new FormData(form);
        const profile = {};
        for (const field of FIELD_DEFINITIONS) profile[field.key] = String(formData.get(`profile.${field.key}`) || "").trim();
        profile.customFields = this.#collectCustomFields(form);
        const ideology = this.#collectIdeology(form);
        if (!ideology) {
            ui.notifications.warn("Each ideology topic may only be selected once.");
            return;
        }
        const dossier = { hiddenDefaultFields: this.#collectHiddenDefaultFields(form) };
        const display = {
            color: String(formData.get("display.color") || FactionEntityModel.getAccent(entity)).trim()
        };
        const extensionValues = Object.fromEntries(
            [...form.querySelectorAll("[data-faction-extension-field]")]
                .map(input => [String(input.dataset.factionExtensionField || ""), String(input.value || "")])
                .filter(([id]) => id)
        );
        try {
            const patch = { display, profile, ideology, dossier };
            let updated = this.#candidate ? this.#applyCandidatePatch(entity, patch) : await updateFaction(entity.id, patch);
            if (this.#candidate) {
                this.#candidate = FactionEditorFieldRegistry.applyToCandidate(updated, extensionValues);
                updated = foundry.utils.deepClone(this.#candidate);
            } else {
                updated = await FactionEditorFieldRegistry.applyToEntity(updated, extensionValues, updateFaction);
            }
            ui.notifications.info(this.#candidate ? "Organization candidate updated." : "Organization updated.");
            this.#onSave?.(updated);
            await this.close();
        } catch (err) {
            console.error(err);
            ui.notifications.error(this.#candidate ? "Failed to update organization candidate." : "Failed to update organization.");
        }
    }

    async #setEmblem(path) {
        const entity = this.#getEntity();
        if (!entity || !path) return;
        if (this.#candidate) {
            this.#candidate.display = { ...(this.#candidate.display || {}), imageSrc: path };
            this.#onSave?.(foundry.utils.deepClone(this.#candidate));
        } else {
            await updateFaction(entity.id, { display: { imageSrc: path } });
            this.#onSave?.(getFaction(this.#factionId));
        }
        this.render({ parts: ["content"] });
    }

    async #setEmblemSelection(selection = {}) {
        const entity = this.#getEntity();
        const moduleId = String(selection.moduleId || "").trim();
        if (!entity || !moduleId) return;
        const display = foundry.utils.deepClone(selection.display || {});
        const moduleData = foundry.utils.deepClone(selection.moduleData || {});
        if (this.#candidate) {
            this.#candidate.display = { ...(this.#candidate.display || {}), ...display };
            this.#candidate.moduleData = { ...(this.#candidate.moduleData || {}), [moduleId]: moduleData };
            this.#onSave?.(foundry.utils.deepClone(this.#candidate));
        } else {
            await updateFaction(entity.id, {
                display,
                moduleData: { [moduleId]: moduleData }
            }, { moduleId });
            this.#onSave?.(getFaction(this.#factionId));
        }
        this.render({ parts: ["content"] });
    }

    #getEntity() { return this.#candidate || getFaction(this.#factionId); }
    #applyCandidatePatch(entity, patch) { this.#candidate = foundry.utils.mergeObject(foundry.utils.deepClone(entity), patch, { inplace: false, recursive: true }); return foundry.utils.deepClone(this.#candidate); }
    #buildOptions(options = [], currentValue = "") {
        const values = new Set((options || []).map(option => String(option?.value || option?.label || option || "").trim()).filter(Boolean));
        if (currentValue) values.add(currentValue);
        return [...values].map(value => ({ value, label: value, selected: value === currentValue }));
    }
    #buildIdeologyRow(selection = {}, index = 0) {
        const topic = this.#ideologyTopics.find(entry => entry.id === selection.topicId) || this.#ideologyTopics[index] || this.#ideologyTopics[0];
        const tenet = topic?.tenets.find(entry => entry.id === selection.tenetId) || topic?.tenets[0];
        return {
            topicId: topic?.id || "",
            topicQuestion: topic?.question || "",
            topics: this.#ideologyTopics.map(entry => ({ id: entry.id, label: entry.label, selected: entry.id === topic?.id })),
            tenetId: tenet?.id || "",
            tenetDescription: tenet?.description || "",
            tenets: (topic?.tenets || []).map(entry => ({ id: entry.id, label: entry.label, selected: entry.id === tenet?.id }))
        };
    }
    #collectIdeology(form) {
        const selections = [...form.querySelectorAll("[data-ideology-row]")].map(row => ({
            topicId: String(row.querySelector("[data-ideology-topic-select]")?.value || "").trim(),
            tenetId: String(row.querySelector("[data-ideology-tenet-select]")?.value || "").trim()
        })).filter(selection => selection.topicId && selection.tenetId);
        if (new Set(selections.map(selection => selection.topicId)).size !== selections.length) return null;
        return { schemaVersion: 1, selections };
    }
    #getHiddenDefaultFields(entity = {}) { return new Set(Array.isArray(entity?.dossier?.hiddenDefaultFields) ? entity.dossier.hiddenDefaultFields : []); }
    #collectHiddenDefaultFields(form) { return [...form.querySelectorAll("input[name='defaultFieldHidden']:checked")].map(input => input.value).filter(Boolean); }
    #collectCustomFields(form) {
        return [...form.querySelectorAll("[data-custom-field-row]")].map(row => ({ id: row.querySelector("[name='customField.id']")?.value || row.dataset.customFieldRow || "", label: row.querySelector("[name='customField.label']")?.value || "", value: row.querySelector("[name='customField.value']")?.value || "" })).filter(field => String(field.label || "").trim() || String(field.value || "").trim());
    }
    #renderIdeologyRow(selection = {}) {
        const row = this.#buildIdeologyRow(selection);
        const safe = value => foundry.utils.escapeHTML(String(value || ""));
        const topicOptions = row.topics.map(option => `<option value="${safe(option.id)}" ${option.selected ? "selected" : ""}>${safe(option.label)}</option>`).join("");
        const tenetOptions = row.tenets.map(option => `<option value="${safe(option.id)}" ${option.selected ? "selected" : ""}>${safe(option.label)}</option>`).join("");
        return `<label><span>Topic</span><select data-ideology-topic-select>${topicOptions}</select><small data-ideology-question>${safe(row.topicQuestion)}</small></label><label><span>Tenet</span><select data-ideology-tenet-select>${tenetOptions}</select><small data-ideology-description>${safe(row.tenetDescription)}</small></label><button type="button" class="faction-ideology-editor-delete" data-action="deleteIdeologyTopic" title="Remove ideology topic" aria-label="Remove ideology topic"><i class="fas fa-trash"></i></button>`;
    }
    #wireIdeologyRow(row) {
        const topicSelect = row.querySelector("[data-ideology-topic-select]");
        const tenetSelect = row.querySelector("[data-ideology-tenet-select]");
        if (!topicSelect || !tenetSelect) return;
        topicSelect.dataset.previousValue = topicSelect.value;
        topicSelect.addEventListener("change", () => {
            const duplicate = [...this.element.querySelectorAll("[data-ideology-topic-select]")].some(select => select !== topicSelect && select.value === topicSelect.value);
            if (duplicate) {
                ui.notifications.warn("Each ideology topic may only be selected once.");
                topicSelect.value = topicSelect.dataset.previousValue || "";
                return;
            }
            topicSelect.dataset.previousValue = topicSelect.value;
            this.#updateIdeologyRow(row);
            this.#refreshIdeologyControls();
        });
        tenetSelect.addEventListener("change", () => this.#updateIdeologyRow(row, { preserveTenet: true }));
        row.querySelector("[data-action='deleteIdeologyTopic']")?.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            row.remove();
            this.#refreshIdeologyControls();
        });
    }
    #updateIdeologyRow(row, { preserveTenet = false } = {}) {
        const topicSelect = row.querySelector("[data-ideology-topic-select]");
        const tenetSelect = row.querySelector("[data-ideology-tenet-select]");
        const topic = this.#ideologyTopics.find(entry => entry.id === topicSelect?.value);
        if (!topic || !tenetSelect) return;
        const requestedTenetId = preserveTenet ? tenetSelect.value : "";
        const tenet = topic.tenets.find(entry => entry.id === requestedTenetId) || topic.tenets[0];
        const safe = value => foundry.utils.escapeHTML(String(value || ""));
        tenetSelect.innerHTML = topic.tenets.map(entry => `<option value="${safe(entry.id)}" ${entry.id === tenet?.id ? "selected" : ""}>${safe(entry.label)}</option>`).join("");
        const question = row.querySelector("[data-ideology-question]");
        const description = row.querySelector("[data-ideology-description]");
        if (question) question.textContent = topic.question;
        if (description) description.textContent = tenet?.description || "";
    }
    #refreshIdeologyControls() {
        const rows = [...this.element.querySelectorAll("[data-ideology-row]")];
        const used = rows.map(row => row.querySelector("[data-ideology-topic-select]")?.value).filter(Boolean);
        for (const row of rows) {
            const select = row.querySelector("[data-ideology-topic-select]");
            if (!select) continue;
            for (const option of select.options) option.disabled = option.value !== select.value && used.includes(option.value);
        }
        const empty = this.element.querySelector("[data-ideology-empty]");
        if (empty) empty.hidden = rows.length > 0;
        const addButton = this.element.querySelector("[data-action='addIdeologyTopic']");
        if (addButton) addButton.disabled = rows.length >= this.#ideologyTopics.length;
    }
    #renderCustomFieldRow(field = {}) {
        const safe = value => foundry.utils.escapeHTML(String(value || ""));
        return `<input type="hidden" name="customField.id" value="${safe(field.id)}"><input type="text" name="customField.label" value="${safe(field.label)}" placeholder="Field" autocomplete="off"><input type="text" name="customField.value" value="${safe(field.value)}" placeholder="Value" autocomplete="off"><button type="button" class="npc-data-custom-field-move" data-action="moveCustomFieldUp" title="Move field up" aria-label="Move field up"><i class="fas fa-arrow-up"></i></button><button type="button" class="npc-data-custom-field-move" data-action="moveCustomFieldDown" title="Move field down" aria-label="Move field down"><i class="fas fa-arrow-down"></i></button><button type="button" class="npc-data-custom-field-delete" data-action="deleteCustomField" title="Delete field" aria-label="Delete field"><i class="fas fa-trash"></i></button>`;
    }
    #wireCustomFieldRow(row) {
        row.querySelector("[data-action='deleteCustomField']")?.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            row.remove();
        });
        row.querySelector("[data-action='moveCustomFieldUp']")?.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            this.#moveCustomField(event.currentTarget, -1);
        });
        row.querySelector("[data-action='moveCustomFieldDown']")?.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            this.#moveCustomField(event.currentTarget, 1);
        });
    }

    #toggleDefaultFieldButton(target) {
        const button = target?.closest?.(".npc-data-field-visibility") || target;
        const input = button?.querySelector?.("input[name='defaultFieldHidden']");
        if (!input) return;
        input.checked = !input.checked;
        button.classList.toggle("is-hidden-field", input.checked);
        button.title = input.checked ? "Show field" : "Hide field";
        button.setAttribute("aria-label", button.title);
        const icon = button.querySelector("i");
        icon?.classList.toggle("fa-eye", !input.checked);
        icon?.classList.toggle("fa-eye-slash", input.checked);
    }
    #moveCustomField(target, direction) {
        const row = target?.closest?.("[data-custom-field-row]");
        if (!row) return;
        if (direction < 0 && row.previousElementSibling) row.previousElementSibling.before(row);
        if (direction > 0 && row.nextElementSibling) row.nextElementSibling.after(row);
    }
    #positionComboMenu(combo) {
        combo.classList.remove("drop-up");
        const menu = combo.querySelector(".npc-data-combo-menu");
        const fields = combo.closest(".npc-data-editor-fields");
        if (!menu || !fields) return;
        const comboRect = combo.getBoundingClientRect();
        const fieldsRect = fields.getBoundingClientRect();
        const menuHeight = Math.min(menu.scrollHeight || 188, 188);
        const spaceBelow = fieldsRect.bottom - comboRect.bottom;
        const spaceAbove = comboRect.top - fieldsRect.top;
        if (spaceBelow < menuHeight + 8 && spaceAbove > spaceBelow) combo.classList.add("drop-up");
    }

    #closeCombos() { for (const combo of this.element.querySelectorAll(".npc-data-combo.open")) combo.classList.remove("open", "drop-up"); }
    #getCenteredPosition() {
        const width = Math.min(1320, Math.max(640, window.innerWidth - 40));
        const height = Math.min(760, Math.max(520, window.innerHeight - 40));
        return {
            width,
            height,
            left: Math.max(20, Math.round((window.innerWidth - width) / 2)),
            top: Math.max(20, Math.round((window.innerHeight - height) / 2))
        };
    }
}
