import { canRerollShipCurrentAssignment, deleteShip, getShip, rerollShipCurrentAssignment, updateShip } from "../../../api/ships.js";
import { openNpcCreator } from "../../../api/npcs.js";
import { openActionMenu, promptTextInput } from "../../../api/ui.js";
import { ConnectionsBoard } from "../../connections/components/ConnectionsBoard.js";
import { ConnectionDropResolver } from "../../connections/services/ConnectionDropResolver.js";
import { ConnectionStore } from "../../connections/services/ConnectionStore.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { ShipCaptainService } from "../../connections/services/ShipCaptainService.js";
import { ShipCrewService } from "../../connections/services/ShipCrewService.js";
import { ShipLocationService } from "../../connections/services/ShipLocationService.js";
import { ShipOwnershipService } from "../../connections/services/ShipOwnershipService.js";
import { NexusMarkerService } from "../../markers/services/NexusMarkerService.js";
import { SiteCoverPicker } from "../../site/applications/SiteCoverPicker.js";
import { SiteCoverCatalog } from "../../site/services/SiteCoverCatalog.js";
import { ShipEntityModel } from "../models/ShipEntityModel.js";
import { ShipProfileViewModel } from "../models/ShipProfileViewModel.js";
import { ShipDomainViewModel } from "../models/ShipDomainViewModel.js";
import { CampaignEntityActorBridge } from "../services/CampaignEntityActorBridge.js";
import { CampaignEntityDossierRefresh } from "../services/CampaignEntityDossierRefresh.js";
import { CampaignEntityJournalStore } from "../services/CampaignEntityJournalStore.js";
import { CampaignEntityVisibilityManager } from "../services/CampaignEntityVisibilityManager.js";
import { PlayerNpcVisibilityDialog } from "../../nexus/applications/PlayerNpcVisibilityDialog.js";
import { PlayerVisibilityInfoPanel } from "../../nexus/applications/PlayerVisibilityInfoPanel.js";
import { DossierTabPreference } from "../../nexus/services/DossierTabPreference.js";
import { applySessionWindowPlacement, rememberSessionWindowPlacement } from "/modules/augur-nexus/scripts/api/window-placement.js";
import { ShipDataEditor } from "./ShipDataEditor.js";
import { ShipImagePicker } from "./ShipImagePicker.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const PLACEMENT_KEY = "augur-nexus.ship-dossier";
const COMPACT_WIDTH = 420;
let shipDossierCompactPreference = false;

export class ShipDossier extends HandlebarsApplicationMixin(ApplicationV2) {
    #shipId = "";
    #activeTab = "profile";
    #connectionsBoard = null;
    #connectionsChangedHook = null;
    #entitiesChangedHook = null;
    #lastModel = null;
    #positionInitialized = false;
    #isCompact = false;
    #expandedPosition = null;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-ship-dossier",
        classes: ["augur-nexus", "npc-dossier", "ship-dossier", "augur-theme-fantasy"],
        tag: "div",
        window: { title: "Ship", resizable: true, minimizable: true },
        position: { width: 1420, height: 780 }
    };

    static PARTS = {
        content: {
            template: "modules/augur-nexus/templates/campaign-entities/ship-dossier.hbs",
            templates: [
                "modules/augur-nexus/templates/connections/connections-board.hbs",
                "modules/augur-nexus/templates/campaign-entities/ship-profile-panel.hbs",
                "modules/augur-nexus/templates/campaign-entities/ship-command-cards.hbs",
                "modules/augur-nexus/templates/campaign-entities/ship-domain-overview.hbs",
                "modules/augur-nexus/templates/site/site-cover-picker.hbs"
            ]
        }
    };

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

    static show({ shipId } = {}) {
        if (!shipId) return null;
        const app = new this({ shipId });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ shipId } = {}, options = {}) {
        super(options);
        this.#shipId = shipId || "";
        this.#isCompact = shipDossierCompactPreference;
        this.#activeTab = DossierTabPreference.getDefaultTab(getShip(this.#shipId));
        this.#connectionsChangedHook = () => this.refresh();
        this.#entitiesChangedHook = event => { if (!event?.entity || event.entity.id === this.#shipId) this.refresh(); };
        Hooks.on("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        Hooks.on("augurNexusCampaignEntitiesChanged", this.#entitiesChangedHook);
        CampaignEntityDossierRefresh.watch(this);
    }

    async _prepareContext() {
        const entity = getShip(this.#shipId);
        if (!entity) return { missing: true, ship: { name: "Missing Ship" }, connections: { board: null } };
        const target = ConnectionTargetResolver.fromCampaignEntity(entity);
        await ShipLocationService.adoptExistingMarkerLocation(target);
        if (!this.#connectionsBoard) this.#connectionsBoard = new ConnectionsBoard({ target, isGM: game.user.isGM });
        else this.#connectionsBoard.setTarget(target);
        const page = CampaignEntityJournalStore.getJournalPage(entity.id);
        const content = await this.#enrichJournalContent(page);
        const linkedActor = await this.#getLinkedActorContext(entity);
        const domain = ShipDomainViewModel.fromEntity(entity, { isGM: game.user.isGM });
        const identity = [
            { label: "Model", value: entity.profile?.model || "Unrecorded" },
            { label: "Class", value: entity.profile?.class || "Unrecorded" },
            { label: "Role", value: entity.profile?.role || "Unrecorded" }
        ];
        const assignment = [
            { label: "Activity", value: entity.profile?.action || "" },
            { label: "Situation", value: entity.profile?.theme || "" },
            { label: "Objective", value: entity.profile?.focus || "" },
            ...ShipEntityModel.getProfileRows(entity)
        ].filter(row => row.value);
        const model = {
            ship: {
                id: entity.id,
                name: ShipEntityModel.getName(entity),
                profile: ShipProfileViewModel.fromEntity(entity, {
                    showTabs: true,
                    showCompactToggle: true,
                    compactMode: this.#isCompact,
                    showActions: game.user.isGM,
                    activeTab: this.#activeTab,
                    linkedActor,
                    showBody: this.#isCompact && this.#activeTab === "profile"
                })
            },
            domain,
            identity,
            assignment,
            canRerollCurrentAssignment: game.user.isGM && canRerollShipCurrentAssignment(entity),
            dossier: {
                isCompact: this.#isCompact,
                isProfileTab: this.#activeTab === "profile",
                isJournalTab: this.#activeTab === "journal",
                isConnectionsTab: this.#activeTab === "connections"
            },
            journal: { hasPage: !!page, entryId: page?.parent?.id || entity.journalEntryId || "", pageId: page?.id || "", content, hasContent: !!String(page?.text?.content || "").trim() },
            connections: { board: this.#connectionsBoard.getContext() },
            backgroundStyle: this.#buildBackgroundStyle(entity),
            isGM: game.user.isGM
        };
        this.#lastModel = model;
        return model;
    }

    _attachPartListeners(partId, htmlElement, options) {
        if (super._attachPartListeners) super._attachPartListeners(partId, htmlElement, options);
        this.#activateActionListeners(htmlElement instanceof HTMLElement ? htmlElement : htmlElement?.[0]);
    }

    _onRender(context, options) {
        super._onRender(context, options);
        if (this.window?.title) this.window.title.textContent = this.title;
        this.#activateActionListeners(this.element);
        this.#connectionsBoard?.activateListeners(this.element, this);
        if (!this.#positionInitialized) {
            const expanded = applySessionWindowPlacement(this, PLACEMENT_KEY, { fallback: "center" });
            if (this.#isCompact) {
                this.#expandedPosition = expanded ? { ...expanded } : this.#getCurrentPosition();
                this.#applyCompactPosition(this.#expandedPosition);
            }
            this.#positionInitialized = true;
        }
    }

    async close(options) {
        if (!this.#isCompact) rememberSessionWindowPlacement(this, PLACEMENT_KEY);
        CampaignEntityDossierRefresh.unwatch(this);
        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() { if (this.rendered !== false) this.render({ parts: ["content"] }); }

    #activateActionListeners(element) {
        if (!element) return;
        const shipImage = element.querySelector("[data-ship-dossier-drag='true']");
        if (shipImage && !shipImage.dataset.shipDossierDragBound) {
            shipImage.dataset.shipDossierDragBound = "true";
            shipImage.addEventListener("dragstart", event => {
                const ship = getShip(this.#shipId);
                const target = ship ? ConnectionTargetResolver.fromCampaignEntity(ship) : null;
                if (!ConnectionTargetResolver.setDragData(event.dataTransfer, target, { effectAllowed: "copy" })) event.preventDefault();
            });
        }
        element.querySelectorAll("[data-action='setShipDossierTab']").forEach(button => {
            if (button.dataset.shipDossierBound) return;
            button.dataset.shipDossierBound = "true";
            button.addEventListener("click", event => {
                event.preventDefault();
                const tab = event.currentTarget.dataset.tab || "profile";
                this.#activeTab = ["journal", "connections"].includes(tab) ? tab : "profile";
                this.render({ parts: ["content"] });
            });
        });
        const compactButton = element.querySelector("[data-action='toggleShipDossierCompact']");
        if (compactButton && !compactButton.dataset.shipDossierBound) compactButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            void this.#toggleCompactMode();
        });
        if (compactButton) compactButton.dataset.shipDossierBound = "true";
        const rerollAssignmentButton = element.querySelector("[data-action='rerollShipCurrentAssignment']");
        if (rerollAssignmentButton && !rerollAssignmentButton.dataset.shipDossierBound) rerollAssignmentButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            void this.#rerollCurrentAssignment(event.currentTarget);
        });
        if (rerollAssignmentButton) rerollAssignmentButton.dataset.shipDossierBound = "true";
        const createCrewButton = element.querySelector("[data-action='createShipCrewMember']");
        if (createCrewButton && !createCrewButton.dataset.shipDossierBound) createCrewButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            this.#createCrewMember();
        });
        if (createCrewButton) createCrewButton.dataset.shipDossierBound = "true";
        const actionsButton = element.querySelector("[data-action='openShipActions']");
        if (actionsButton && !actionsButton.dataset.shipDossierBound) actionsButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            this.#openActions(event.currentTarget);
        });
        if (actionsButton) actionsButton.dataset.shipDossierBound = "true";

        const createJournalButton = element.querySelector("[data-action='createShipJournal']");
        if (createJournalButton && !createJournalButton.dataset.shipDossierBound) createJournalButton.addEventListener("click", event => {
            event.preventDefault();
            this.#editJournal({ create: true });
        });
        if (createJournalButton) createJournalButton.dataset.shipDossierBound = "true";

        const openJournalButton = element.querySelector("[data-action='openShipJournal']");
        if (openJournalButton && !openJournalButton.dataset.shipDossierBound) openJournalButton.addEventListener("click", event => {
            event.preventDefault();
            this.#editJournal({ create: false });
        });
        if (openJournalButton) openJournalButton.dataset.shipDossierBound = "true";

        const openLinkedActorButton = element.querySelector("[data-action='openShipLinkedActor']");
        if (openLinkedActorButton && !openLinkedActorButton.dataset.shipDossierBound) openLinkedActorButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            void this.#openLinkedActor();
        });
        if (openLinkedActorButton) openLinkedActorButton.dataset.shipDossierBound = "true";

        const linkActorButton = element.querySelector("[data-action='linkShipActor']");
        if (linkActorButton && !linkActorButton.dataset.shipDossierBound) linkActorButton.addEventListener("click", event => {
            event.preventDefault();
            event.stopPropagation();
            void this.#linkActor();
        });
        if (linkActorButton) linkActorButton.dataset.shipDossierBound = "true";

        element.querySelectorAll("[data-ship-domain-node]").forEach(card => {
            if (card.dataset.shipDomainBound) return;
            card.dataset.shipDomainBound = "true";
            card.addEventListener("click", async event => {
                const node = ConnectionStore.getNode(event.currentTarget.dataset.shipDomainNode || "");
                if (node) await ConnectionTargetResolver.openNode(node);
            });
            if (card.draggable) card.addEventListener("dragstart", event => {
                const node = ConnectionStore.getNode(card.dataset.shipDomainNode || "");
                if (node) ConnectionTargetResolver.setDragData(event.dataTransfer, node, { effectAllowed: "copyMove" });
            });
        });
        element.querySelectorAll("[data-ship-domain-menu]").forEach(card => {
            if (card.dataset.shipDomainMenuBound) return;
            card.dataset.shipDomainMenuBound = "true";
            card.addEventListener("contextmenu", event => {
                event.preventDefault();
                event.stopPropagation();
                this.#openDomainCardMenu(event.currentTarget, event.currentTarget.dataset.shipDomainMenu || "");
            });
        });
        this.#activateDomainDrops(element);
    }

    #openDomainCardMenu(anchor, kind) {
        if (!game.user.isGM) return;
        const item = kind === "owner"
            ? { id: "remove-owner", label: "Remove Owner", icon: "fas fa-user-slash", danger: true, onSelect: () => this.#clearShipOwner() }
            : kind === "captain"
                ? { id: "remove-captain", label: "Remove Captain", icon: "fas fa-user-minus", danger: true, onSelect: () => this.#clearShipCaptain() }
                : null;
        if (!item) return;
        openActionMenu({ anchor, className: "ship-dossier-actions-menu", items: [item] });
    }

    async #toggleCompactMode() {
        if (!this.#isCompact) {
            rememberSessionWindowPlacement(this, PLACEMENT_KEY);
            this.#expandedPosition = this.#getCurrentPosition();
        }
        this.#isCompact = !this.#isCompact;
        shipDossierCompactPreference = this.#isCompact;
        await this.render({ parts: ["content"] });
        if (this.#isCompact) this.#applyCompactPosition(this.#expandedPosition);
        else if (this.#expandedPosition) this.setPosition(this.#expandedPosition);
        else applySessionWindowPlacement(this, PLACEMENT_KEY, { fallback: "center" });
    }

    async #rerollCurrentAssignment(button) {
        if (!game.user.isGM) return;
        button.disabled = true;
        try {
            const updated = await rerollShipCurrentAssignment(this.#shipId);
            if (!updated) ui.notifications.warn("This ship's generator cannot reroll its Current Assignment.");
            else this.refresh();
        } catch (error) {
            console.error("Augur Nexus | Failed to reroll ship Current Assignment", error);
            ui.notifications.error(error?.message || "The ship's Current Assignment could not be rerolled.");
        } finally {
            button.disabled = false;
        }
    }

    #applyCompactPosition(source = null) {
        const current = source || this.#getCurrentPosition() || {};
        const oldWidth = Number(current.width || this.position?.width || this.element?.offsetWidth || COMPACT_WIDTH);
        const oldLeft = Number(current.left ?? this.position?.left ?? 20);
        const width = Math.min(COMPACT_WIDTH, Math.max(320, window.innerWidth - 40));
        const right = oldLeft + oldWidth;
        const left = Math.min(Math.max(20, right - width), Math.max(20, window.innerWidth - width - 20));
        this.setPosition({
            left,
            top: Number(current.top ?? this.position?.top ?? 20),
            width,
            height: Number(current.height || this.position?.height || this.element?.offsetHeight || 780)
        });
    }

    #getCurrentPosition() {
        const position = this.position || {};
        const left = Number(position.left ?? this.element?.offsetLeft ?? NaN);
        const top = Number(position.top ?? this.element?.offsetTop ?? NaN);
        if (!Number.isFinite(left) || !Number.isFinite(top)) return null;
        return {
            left,
            top,
            width: Number(position.width || this.element?.offsetWidth || 1420),
            height: Number(position.height || this.element?.offsetHeight || 780)
        };
    }

    #activateDomainDrops(element) {
        if (!game.user.isGM) return;
        element.querySelectorAll("[data-ship-domain-drop]").forEach(zone => {
            if (zone.dataset.shipDomainDropBound) return;
            zone.dataset.shipDomainDropBound = "true";
            zone.addEventListener("dragover", event => {
                event.preventDefault();
                event.stopPropagation();
                if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
                zone.classList.add("is-ship-drop-target");
            });
            zone.addEventListener("dragleave", event => {
                if (!zone.contains(event.relatedTarget)) zone.classList.remove("is-ship-drop-target");
            });
            zone.addEventListener("drop", event => {
                event.preventDefault();
                event.stopPropagation();
                zone.classList.remove("is-ship-drop-target");
                void this.#handleDomainDrop(event, zone.dataset.shipDomainDrop || "");
            });
        });
    }

    async #handleDomainDrop(event, action) {
        const ship = getShip(this.#shipId);
        const shipTarget = ship ? ConnectionTargetResolver.fromCampaignEntity(ship) : null;
        const relatedTarget = await ConnectionDropResolver.fromEvent(event);
        if (!shipTarget || !relatedTarget) {
            ui.notifications.warn("Drop a Nexus entity here.");
            return;
        }
        try {
            if (action === "captain" || action === "crew") {
                if (relatedTarget.kind !== "nexus-entity" || relatedTarget.entityType !== "npc") throw new Error("Only a Nexus Person can serve aboard a Ship.");
                if (action === "captain") await ShipCaptainService.setCaptain(shipTarget, relatedTarget);
                else await ShipCrewService.addCrew(shipTarget, relatedTarget);
            } else if (action === "owner") {
                if (relatedTarget.kind !== "nexus-entity" || !["npc", "faction"].includes(relatedTarget.entityType)) throw new Error("A Ship owner must be a Person or Organization.");
                await ShipOwnershipService.setOwner(shipTarget, relatedTarget);
            } else if (action === "location") {
                const isPlace = relatedTarget.kind === "nexus-site" || relatedTarget.kind === "nexus-scene" || (relatedTarget.kind === "nexus-entity" && relatedTarget.entityType === "location");
                if (!isPlace) throw new Error("Current Location requires a Nexus Place.");
                await ShipLocationService.setCurrentLocation(shipTarget, relatedTarget, { source: "manual" });
            } else if (action === "ties") {
                await ConnectionStore.addConnection(shipTarget, relatedTarget);
            }
            this.refresh();
        } catch (error) {
            console.error("Augur Nexus | Failed to update Ship dossier relationships.", error);
            ui.notifications.error(error?.message || "The Ship relationship could not be updated.");
        }
    }

    #createCrewMember() {
        const ship = getShip(this.#shipId);
        if (!game.user.isGM || !ship) return;
        void openNpcCreator({
            openAfterCreate: false,
            onCreated: async npc => {
                const shipTarget = ConnectionTargetResolver.fromCampaignEntity(ship);
                const npcTarget = ConnectionTargetResolver.fromCampaignEntity(npc);
                if (shipTarget && npcTarget) await ShipCrewService.addCrew(shipTarget, npcTarget);
                this.refresh();
            }
        });
    }

    #openActions(anchor) {
        if (!game.user.isGM) return;
        const entity = getShip(this.#shipId);
        if (!entity) return;
        openActionMenu({
            anchor,
            className: "ship-dossier-actions-menu",
            items: [
                { id: "rename", label: "Rename", icon: "fas fa-pen", onSelect: () => this.#renameShip() },
                { id: "edit-ship", label: "Edit Ship", icon: "fas fa-sliders", onSelect: () => this.#editShip() },
                { id: "change-model", label: "Change Model", icon: "fas fa-rocket", onSelect: () => this.#changeShipImage() },
                { id: "open-journal", label: "Edit Journal", icon: "fas fa-book-open", onSelect: () => this.#editJournal() },
                { id: "linked-actor", label: entity.projections?.actorUuid ? "Linked Actor" : "Link Actor", icon: "fas fa-user-circle", onSelect: () => this.#manageActorLink() },
                ...(ShipOwnershipService.getOwner(ConnectionTargetResolver.fromCampaignEntity(entity), { includeHidden: true }) ? [{ id: "clear-owner", label: "Clear Ship Owner", icon: "fas fa-shield-halved", onSelect: () => this.#clearShipOwner() }] : []),
                ...(ShipCaptainService.getCaptain(ConnectionTargetResolver.fromCampaignEntity(entity), { includeHidden: true }) ? [{ id: "clear-captain", label: "Remove Captain", icon: "fas fa-user-minus", onSelect: () => this.#clearShipCaptain() }] : []),
                ...(ShipLocationService.getCurrentLocation(ConnectionTargetResolver.fromCampaignEntity(entity), { includeHidden: true }) ? [{ id: "clear-location", label: "Clear Current Location", icon: "fas fa-location-crosshairs", onSelect: () => this.#clearShipLocation() }] : []),
                {
                    id: "default-dossier-tab",
                    label: `Default Tab: ${DossierTabPreference.getTabLabel(DossierTabPreference.getDefaultTab(entity))}`,
                    icon: "fas fa-table-columns",
                    onSelect: () => this.#openDefaultDossierTabMenu(anchor)
                },
                { id: "change-cover", label: "Change Cover Image", icon: "fas fa-image", onSelect: () => this.#changeCoverImage() },
                ...(ShipEntityModel.getCoverImage(entity) ? [{ id: "clear-cover", label: "Clear Cover Image", icon: "fas fa-rotate-left", onSelect: () => this.#setCoverImage("") }] : []),
                {
                    id: "player-ship-visibility",
                    label: `Visible To Players: ${CampaignEntityVisibilityManager.getPlayerVisibilityLabel(entity)}`,
                    icon: CampaignEntityVisibilityManager.getPlayerVisibilityIcon(entity),
                    onSelect: () => this.#openShipVisibilityMenu(anchor)
                },
                { id: "delete", label: "Delete Ship", status: "Permanent", icon: "fas fa-skull-crossbones", danger: true, className: "permanent-danger", onSelect: () => this.#deleteShip() }
            ]
        });
    }

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

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

    #openShipVisibilityMenu(anchor) {
        const entity = getShip(this.#shipId);
        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.#setShipPlayerVisibility("inherit") },
                { id: "show", label: current === "show" ? "Yes (Current)" : "Yes", icon: "fas fa-eye", onSelect: () => this.#setShipPlayerVisibility("show") },
                { id: "hide", label: current === "hide" ? "No (Current)" : "No", icon: "fas fa-eye-slash", onSelect: () => this.#setShipPlayerVisibility("hide") },
                { id: "change-global", label: "Change Global Setting...", icon: "fas fa-sliders", onSelect: () => PlayerNpcVisibilityDialog.show("ship") },
                { id: "player-visibility-info", label: "What's this?", icon: "fas fa-circle-info", onSelect: () => PlayerVisibilityInfoPanel.show("campaignEntityVisibility") }
            ]
        });
    }

    async #getLinkedActorContext(entity) {
        const actor = await CampaignEntityActorBridge.getLinkedActor(entity);
        if (!actor) return null;
        return {
            name: actor.name || "Actor",
            uuid: actor.uuid || "",
            type: actor.type || "Actor"
        };
    }

    async #openLinkedActor() {
        const entity = getShip(this.#shipId);
        if (!entity) return;
        const actor = await CampaignEntityActorBridge.getLinkedActor(entity);
        if (!actor) {
            ui.notifications.warn("No linked Actor was found for this ship.");
            return;
        }
        actor.sheet?.render(true);
    }

    async #linkActor() {
        await this.#manageActorLink();
    }

    async #manageActorLink() {
        const entity = getShip(this.#shipId);
        if (!game.user.isGM || !entity) return;
        const result = await CampaignEntityActorBridge.manageActorLink(entity);
        if (result?.action && result.action !== "cancel" && result.action !== "open") this.refresh();
    }

    async #setShipPlayerVisibility(value) {
        const entity = getShip(this.#shipId);
        if (!game.user.isGM || !entity) return;
        await CampaignEntityVisibilityManager.setPlayerVisibilityOverride(entity.id, "ship", value);
        this.refresh();
    }

    async #clearShipOwner() {
        const entity = getShip(this.#shipId);
        const target = entity ? ConnectionTargetResolver.fromCampaignEntity(entity) : null;
        if (!target) return;
        await ShipOwnershipService.clearOwner(target);
        this.refresh();
    }

    async #clearShipCaptain() {
        const entity = getShip(this.#shipId);
        const target = entity ? ConnectionTargetResolver.fromCampaignEntity(entity) : null;
        if (!target) return;
        await ShipCaptainService.clearCaptain(target);
        this.refresh();
    }

    async #clearShipLocation() {
        const entity = getShip(this.#shipId);
        const target = entity ? ConnectionTargetResolver.fromCampaignEntity(entity) : null;
        if (!entity || !target) return;
        const current = ShipLocationService.getCurrentLocation(target, { includeHidden: true });
        if (current?.source === "marker" && current.markerId) {
            const ref = (entity.markerRefs || []).find(candidate => candidate.markerId === current.markerId);
            const scene = ref?.sceneId ? game.scenes.get(ref.sceneId) : null;
            const tile = scene && ref?.documentId ? scene.tiles.get(ref.documentId) : null;
            if (tile) await NexusMarkerService.deleteMarker(tile, { notify: false, scene });
            else {
                await NexusMarkerService.removeOwnerMarkerRef({ kind: "campaign-entity", entityType: "ship", entityId: entity.id }, current.markerId);
                await ShipLocationService.clearCurrentLocation(target, { markerId: current.markerId });
            }
        } else {
            await ShipLocationService.clearCurrentLocation(target);
        }
        this.refresh();
    }

    async #renameShip() {
        const entity = getShip(this.#shipId);
        if (!entity) return;
        const name = await promptTextInput({ title: "Rename Ship", label: "Name", value: ShipEntityModel.getName(entity), confirmLabel: "Rename" });
        if (!name) return;
        await updateShip(entity.id, { display: { name } });
        this.refresh();
    }

    #editShip() { const entity = getShip(this.#shipId); if (entity) ShipDataEditor.show({ shipId: entity.id, onSave: () => this.refresh() }); }

    #changeShipImage() {
        const entity = getShip(this.#shipId);
        if (!entity) return;
        const moduleData = entity.moduleData?.[entity.sourceModule] || entity.moduleData?.["augur-scifi"] || {};
        new ShipImagePicker({
            currentShipImageSrc: ShipEntityModel.getImage(entity),
            selectedModelId: moduleData.modelId || "",
            onSelect: selection => this.#setShipImage(selection)
        }).render(true, { focus: true });
    }

    async #setShipImage(selection = {}) {
        const entity = getShip(this.#shipId);
        if (!entity || !selection.src) return;
        const moduleId = entity.sourceModule || "augur-scifi";
        const patch = {
            display: { imageSrc: selection.src },
            moduleData: { [moduleId]: { ...(entity.moduleData?.[moduleId] || {}), modelId: selection.modelId || "", styleId: selection.styleId || "" } }
        };
        if (!selection.custom && selection.modelLabel) patch.profile = { model: selection.modelLabel };
        await updateShip(entity.id, patch, { moduleId });
        this.refresh();
    }

    #changeCoverImage() { const entity = getShip(this.#shipId); if (entity) SiteCoverPicker.show({ current: ShipEntityModel.getCoverImage(entity), onSelect: path => this.#setCoverImage(path || "") }); }
    async #setCoverImage(path) { const entity = getShip(this.#shipId); if (entity) { await updateShip(entity.id, { display: { coverImageSrc: path || "" } }); this.refresh(); } }

    async #editJournal({ create = true } = {}) {
        if (!game.user.isGM) return;
        const page = create ? await CampaignEntityJournalStore.getOrCreateJournalPage(this.#shipId) : CampaignEntityJournalStore.getJournalPage(this.#shipId);
        if (!page?.parent) { ui.notifications.warn("No journal page was found for this ship."); return; }
        page.sheet?.render(true);
    }

    async #deleteShip() {
        const entity = getShip(this.#shipId);
        if (!entity) return;
        const confirmed = await foundry.applications.api.DialogV2.confirm({ window: { title: "Delete Ship" }, content: `<p>Delete <strong>${foundry.utils.escapeHTML(ShipEntityModel.getName(entity))}</strong>?</p>`, modal: true, rejectClose: false, yes: { label: "Delete" }, no: { label: "Cancel" } });
        if (!confirmed) return;
        await deleteShip(entity.id);
        await this.close();
    }

    async #enrichJournalContent(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 (_err) { return content; }
    }

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

}
