import { canRerollLocationCurrentThread, deleteLocation, getLocation, getLocationViews, openLocation, openLocationView, rerollLocationCurrentThread, updateLocation } from "../../../api/locations.js";
import { openNpcCreator } from "../../../api/npcs.js";
import { openShopCreator } from "../../../api/shops.js";
import { addConnection } from "../../../api/connections.js";
import { openActionMenu, promptTextInput } from "../../../api/ui.js";
import { ConnectionsBoard } from "../../connections/components/ConnectionsBoard.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { PlayerNpcVisibilityDialog } from "../../nexus/applications/PlayerNpcVisibilityDialog.js";
import { PlayerLocationDisclosureDialog } from "../../nexus/applications/PlayerLocationDisclosureDialog.js";
import { PlayerVisibilityInfoPanel } from "../../nexus/applications/PlayerVisibilityInfoPanel.js";
import { DossierTabPreference } from "../../nexus/services/DossierTabPreference.js";
import { SiteCoverPicker } from "../../site/applications/SiteCoverPicker.js";
import { SiteIconPicker } from "../../site/applications/SiteIconPicker.js";
import { SitePanel } from "../../site/applications/SitePanel.js";
import { SiteCoverCatalog } from "../../site/services/SiteCoverCatalog.js";
import { LocationEntityModel } from "../models/LocationEntityModel.js";
import { LocationProfileViewModel } from "../models/LocationProfileViewModel.js";
import { applySessionWindowPlacement, rememberSessionWindowPlacement } from "/modules/augur-nexus/scripts/api/window-placement.js";
import { CampaignEntityDossierRefresh } from "../services/CampaignEntityDossierRefresh.js";
import { CampaignEntityVisibilityManager } from "../services/CampaignEntityVisibilityManager.js";
import { CampaignEntityDisclosureManager } from "../services/CampaignEntityDisclosureManager.js";
import { LocationEntityStore } from "../services/LocationEntityStore.js";
import { LocationActionRegistry } from "../services/LocationActionRegistry.js";
import { LocationSceneWorkflow } from "../services/LocationSceneWorkflow.js";
import { LocationDataEditor } from "./LocationDataEditor.js";
import { EntityChroniclePanel } from "../../region-population/components/EntityChroniclePanel.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const PLACEMENT_KEY = "augur-nexus.location-dossier";

export class LocationDossier extends HandlebarsApplicationMixin(ApplicationV2) {
    #locationId = "";
    #activeTab = "profile";
    #connectionsBoard = null;
    #chroniclePanel = null;
    #connectionsChangedHook = null;
    #entitiesChangedHook = null;
    #lastModel = null;
    #positionInitialized = false;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-location-dossier",
        classes: ["augur-nexus", "npc-dossier", "location-dossier"],
        tag: "div",
        window: { title: "Location", resizable: true, minimizable: true },
        position: { width: 680, height: 600 },
        actions: {
            setLocationDossierTab: LocationDossier._onSetTab,
            openLocationActions: LocationDossier._onActions,
            editLocation: LocationDossier._onEdit,
            openLocationJournal: LocationDossier._onJournal,
            createLocationJournal: LocationDossier._onJournal,
            openLocationScene: LocationDossier._onOpenScene,
            openLocationCreateMenu: LocationDossier._onCreateMenu,
            openRegisteredLocationView: LocationDossier._onOpenRegisteredView,
            rerollLocationCurrentThread: LocationDossier._onRerollCurrentThread,
            deleteLocation: LocationDossier._onDelete
        }
    };

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

    get title() { return this.#lastModel?.location?.name || "Location"; }

    static show({ locationId = "" } = {}) {
        if (!locationId) return null;
        const entity = getLocation(locationId);
        if (!entity || !CampaignEntityVisibilityManager.canUserSee(entity, game.user)) return null;
        if (!CampaignEntityDisclosureManager.canUserViewDetails(entity, game.user)) return openLocation(entity);
        const app = new this({ locationId });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ locationId = "" } = {}, options = {}) {
        super(options);
        this.#locationId = locationId;
        this.#chroniclePanel = new EntityChroniclePanel({ entityId: this.#locationId });
        this.#activeTab = DossierTabPreference.getDefaultTab(getLocation(this.#locationId));
        this.#connectionsChangedHook = () => this.refresh();
        this.#entitiesChangedHook = event => {
            if (!event?.entity || event.entity.id === this.#locationId) this.refresh();
        };
        Hooks.on("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        Hooks.on("augurNexusCampaignEntitiesChanged", this.#entitiesChangedHook);
        CampaignEntityDossierRefresh.watch(this);
    }

    async _prepareContext() {
        const entity = getLocation(this.#locationId);
        if (!entity) return { missing: true, location: { name: "Missing Location" }, connections: { board: null } };

        const target = ConnectionTargetResolver.fromCampaignEntity(entity);
        if (!this.#connectionsBoard) this.#connectionsBoard = new ConnectionsBoard({ target, isGM: game.user.isGM });
        else this.#connectionsBoard.setTarget(target);

        const page = LocationEntityStore.getJournalPage(entity.id);
        const hasChronicle = this.#chroniclePanel.hasEvents();
        const canRerollCurrentThread = game.user.isGM && canRerollLocationCurrentThread(entity);
        if (this.#activeTab === "chronicle" && !hasChronicle) this.#activeTab = "profile";
        const model = {
            missing: false,
            isGM: game.user.isGM,
            location: {
                id: entity.id,
                name: LocationEntityModel.getName(entity),
                profile: LocationProfileViewModel.fromEntity(entity, {
                    activeTab: this.#activeTab,
                    hasChronicle,
                    showTabs: true,
                    showActions: game.user.isGM,
                    showBody: this.#activeTab === "profile",
                    canRerollCurrentThread
                })
            },
            dossier: {
                isProfileTab: this.#activeTab === "profile",
                isJournalTab: this.#activeTab === "journal",
                isChronicleTab: this.#activeTab === "chronicle"
            },
            chronicleHtml: hasChronicle ? await this.#chroniclePanel.render() : "",
            journal: {
                hasPage: !!page,
                entryId: page?.parent?.id || entity.journalEntryId || "",
                pageId: page?.id || "",
                hasContent: !!String(page?.text?.content || "").trim(),
                content: await this.#enrich(page)
            },
            connections: { board: this.#connectionsBoard.getContext() },
            locationViews: getLocationViews(entity),
            sceneControl: LocationSceneWorkflow.getControlModel(entity.id),
            backgroundStyle: this.#buildBackgroundStyle(entity)
        };
        model.hasProfileActions = model.locationViews.length > 0 || model.sceneControl.canOpen;
        this.#lastModel = model;
        return model;
    }

    _onRender(context, options) {
        super._onRender(context, options);
        if (this.window?.title) this.window.title.textContent = this.title;
        this.#connectionsBoard?.activateListeners(this.element, this);
        this.#chroniclePanel?.activateListeners(this.element, this);
        if (!this.#positionInitialized) {
            applySessionWindowPlacement(this, PLACEMENT_KEY, { fallback: "center" });
            this.#positionInitialized = true;
        }
    }

    async close(options) {
        rememberSessionWindowPlacement(this, PLACEMENT_KEY);
        CampaignEntityDossierRefresh.unwatch(this);
        this.#chroniclePanel?.destroy();
        if (this.#connectionsChangedHook) Hooks.off("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        if (this.#entitiesChangedHook) Hooks.off("augurNexusCampaignEntitiesChanged", this.#entitiesChangedHook);
        return super.close(options);
    }

    matchesCampaignEntityJournal({ entryId, pageId } = {}) {
        const journal = this.#lastModel?.journal || null;
        if (!journal?.entryId) return false;
        if (pageId) return journal.pageId === pageId;
        return journal.entryId === entryId;
    }

    refreshCampaignEntityDossier() { this.refresh(); }
    refresh() {
        const entity = getLocation(this.#locationId);
        if (!entity || !CampaignEntityVisibilityManager.canUserSee(entity, game.user)) {
            void this.close();
            return;
        }
        if (!CampaignEntityDisclosureManager.canUserViewDetails(entity, game.user)) {
            void this.close().then(() => openLocation(entity));
            return;
        }
        if (this.rendered !== false) this.render({ parts: ["content"] });
    }

    static async _onOpenRegisteredView(event, target) {
        const viewId = target?.dataset?.locationViewId || "";
        const entity = getLocation(this.#locationId);
        if (viewId && entity) await openLocationView(viewId, entity);
    }

    static async _onRerollCurrentThread(_event, target) {
        if (!game.user.isGM || target?.disabled) return;
        target.disabled = true;
        try {
            const updated = await rerollLocationCurrentThread(this.#locationId);
            if (!updated) ui.notifications.warn("This location's generator cannot reroll its Current Thread.");
            this.refresh();
        } catch (error) {
            console.error("Augur Nexus | Failed to reroll location Current Thread", error);
            ui.notifications.error("The Current Thread could not be rerolled.");
        } finally {
            target.disabled = false;
        }
    }

    static async _onSetTab(event, target) {
        const tab = target?.dataset?.tab || "profile";
        this.#activeTab = ["journal", "chronicle"].includes(tab) ? tab : "profile";
        this.render({ parts: ["content"] });
    }

    static async _onEdit() {
        LocationDataEditor.show({ locationId: this.#locationId, onSave: () => this.refresh() });
    }

    static async _onJournal() {
        const page = await LocationEntityStore.getOrCreateJournalPage(this.#locationId);
        page?.sheet?.render(true);
    }

    static async _onOpenScene() {
        await LocationSceneWorkflow.open(this.#locationId);
    }

    static async _onCreateMenu(_event, target) {
        if (!game.user.isGM) return;
        const location = getLocation(this.#locationId);
        const locationActions = LocationActionRegistry.listFor(location, { onChanged: () => this.refresh() });
        openActionMenu({
            anchor: target,
            className: "location-dossier-create-menu",
            items: [
                { id: "person", label: "Person", icon: "fas fa-user-plus", onSelect: () => this.#createPerson() },
                { id: "shop", label: "Shop", icon: "fas fa-store", onSelect: () => openShopCreator({ locationId: this.#locationId, onSaved: () => this.refresh() }) },
                ...locationActions
            ]
        });
    }

    async #createPerson() {
        const location = getLocation(this.#locationId);
        if (!location) return;
        void openNpcCreator({
            onCreated: async npc => {
                const locationTarget = ConnectionTargetResolver.fromCampaignEntity(location);
                const npcTarget = ConnectionTargetResolver.fromCampaignEntity(npc);
                if (locationTarget && npcTarget) {
                    await addConnection(locationTarget, npcTarget, {
                        sourceView: { category: "npc", role: "Resident" },
                        targetView: { category: "place", role: "Located At" }
                    });
                }
                this.refresh();
            }
        });
    }

    static async _onActions(event, target) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        const locationViews = getLocationViews(entity);
        openActionMenu({
            anchor: target,
            className: "location-dossier-actions-menu",
            items: [
                { id: "rename", label: "Rename", icon: "fas fa-pen", onSelect: () => this.#renameLocation() },
                { id: "edit", label: "Edit Location", icon: "fas fa-sliders", onSelect: () => LocationDossier._onEdit.call(this) },
                { id: "change-icon", label: "Change Icon", icon: "fas fa-map-location-dot", onSelect: () => this.#changeIcon() },
                { id: "open-journal", label: "Edit Journal", icon: "fas fa-book-open", onSelect: () => LocationDossier._onJournal.call(this) },
                ...locationViews.map(view => ({ id: `location-view:${view.id}`, label: view.label, icon: view.icon, onSelect: () => openLocationView(view.id, entity) })),
                ...LocationSceneWorkflow.getMenuItems(entity.id, { onChanged: () => this.refresh() }),
                {
                    id: "default-dossier-tab",
                    label: `Default Tab: ${DossierTabPreference.getTabLabel(DossierTabPreference.getDefaultTab(entity))}`,
                    icon: "fas fa-table-columns",
                    onSelect: () => this.#openDefaultDossierTabMenu(target)
                },
                { id: "change-cover", label: "Change Cover Image", icon: "fas fa-image", onSelect: () => this.#changeCoverImage() },
                ...(LocationEntityModel.getCoverImage(entity) ? [{ id: "clear-cover", label: "Clear Cover Image", icon: "fas fa-rotate-left", onSelect: () => this.#setCoverImage("") }] : []),
                {
                    id: "player-location-visibility",
                    label: `Visible To Players: ${CampaignEntityVisibilityManager.getPlayerVisibilityLabel(entity)}`,
                    icon: CampaignEntityVisibilityManager.getPlayerVisibilityIcon(entity),
                    onSelect: () => this.#openLocationVisibilityMenu(target)
                },
                {
                    id: "player-location-disclosure",
                    label: `Player Details: ${CampaignEntityDisclosureManager.getPlayerDisclosureLabel(entity, { includeEffective: true })}`,
                    icon: CampaignEntityDisclosureManager.getPlayerDisclosureIcon(entity),
                    onSelect: () => PlayerLocationDisclosureDialog.openEntityMenu({
                        anchor: target,
                        location: entity,
                        onChanged: () => this.refresh()
                    })
                },
                { id: "delete", label: "Delete Location", status: "Permanent", icon: "fas fa-skull-crossbones", danger: true, className: "permanent-danger", onSelect: () => LocationDossier._onDelete.call(this) }
            ]
        });
    }

    async #renameLocation() {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        const name = await promptTextInput({
            title: "Rename Location",
            label: "Name",
            value: LocationEntityModel.getName(entity),
            confirmLabel: "Rename"
        });
        if (!name) return;
        await updateLocation(entity.id, { display: { name } });
        this.refresh();
    }

    async #changeIcon() {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        const icons = await SitePanel.getAllBuiltInIcons();
        const currentSrc = LocationEntityModel.getImage(entity);
        const currentIconId = icons.find(icon => icon.src === currentSrc)?.id || (currentSrc ? "custom" : null);
        new SiteIconPicker(icons, currentIconId, "landmark", selection => {
            const nextSrc = typeof selection === "object" && selection?.id === "custom"
                ? selection.src || ""
                : icons.find(icon => icon.id === selection)?.src || currentSrc;
            if (nextSrc) void this.#setIcon(nextSrc);
        }, {
            currentCustomIconSrc: currentIconId === "custom" ? currentSrc : "",
            allBuiltInIcons: icons,
            showThemeIconsOnly: false,
            filterByRole: false
        }).render(true);
    }

    async #setIcon(path) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity || !path) return;
        await updateLocation(entity.id, { display: { imageSrc: path, thumbnailSrc: "" } });
        this.refresh();
    }

    #openDefaultDossierTabMenu(anchor) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        openActionMenu({
            anchor,
            className: "location-dossier-actions-menu",
            items: DossierTabPreference.buildMenuItems(
                DossierTabPreference.getDefaultTab(entity),
                tab => this.#setDefaultDossierTab(tab)
            )
        });
    }

    async #setDefaultDossierTab(tab) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        const defaultTab = DossierTabPreference.normalizeTab(tab);
        await updateLocation(entity.id, { dossier: { defaultTab } });
        this.#activeTab = defaultTab;
        this.refresh();
    }

    #changeCoverImage() {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        SiteCoverPicker.show({
            current: LocationEntityModel.getCoverImage(entity),
            onSelect: path => this.#setCoverImage(path || "")
        });
    }

    async #setCoverImage(path) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        await updateLocation(entity.id, { display: { coverImageSrc: path || "" } });
        this.refresh();
    }

    #openLocationVisibilityMenu(anchor) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        const current = CampaignEntityVisibilityManager.getPlayerVisibilityOverride(entity);
        openActionMenu({
            anchor,
            className: "nexus-scene-player-access-menu",
            items: [
                { id: "inherit", label: current === "inherit" ? "Global (Current)" : "Global", icon: "fas fa-layer-group", onSelect: () => this.#setLocationPlayerVisibility("inherit") },
                { id: "show", label: current === "show" ? "Yes (Current)" : "Yes", icon: "fas fa-eye", onSelect: () => this.#setLocationPlayerVisibility("show") },
                { id: "hide", label: current === "hide" ? "No (Current)" : "No", icon: "fas fa-eye-slash", onSelect: () => this.#setLocationPlayerVisibility("hide") },
                { id: "change-global", label: "Change Global Setting...", icon: "fas fa-sliders", onSelect: () => PlayerNpcVisibilityDialog.show("location") },
                { id: "player-visibility-info", label: "What's this?", icon: "fas fa-circle-info", onSelect: () => PlayerVisibilityInfoPanel.show("campaignEntityVisibility") }
            ]
        });
    }

    async #setLocationPlayerVisibility(value) {
        const entity = getLocation(this.#locationId);
        if (!game.user.isGM || !entity) return;
        await CampaignEntityVisibilityManager.setPlayerVisibilityOverride(entity.id, "location", value);
        this.refresh();
    }

    static async _onDelete() {
        const entity = getLocation(this.#locationId);
        if (!entity) return;
        if (await deleteLocation(entity.id)) await this.close();
    }

    async #enrich(page) {
        const content = page?.text?.content || "";
        if (!String(content).trim()) return "";
        try {
            return foundry.applications.ux.TextEditor.implementation.enrichHTML(content, { relativeTo: page, secrets: page.isOwner });
        } catch (_error) {
            return content;
        }
    }

    #buildBackgroundStyle(entity) {
        const cover = LocationEntityModel.getCoverImage(entity)
            || SiteCoverCatalog.getAbstractCover(entity?.id || LocationEntityModel.getName(entity) || "location");
        return cover ? `background-image: url('${cover}');` : "";
    }
}
