import { canUserViewLocationDetails, getLocation } from "/modules/augur-nexus/scripts/api/locations.js";
import { getShopsForParentLocation } from "/modules/augur-nexus/scripts/api/shops.js";
import { TradeBasinEconomyService } from "../services/TradeBasinEconomyService.js";
import { TradeGoodsCatalog } from "../services/TradeGoodsCatalog.js";
import { TradeRegionSceneService } from "../services/TradeRegionSceneService.js";
import { SettlementMarketplaceModel } from "./SettlementMarketplaceModel.js";

export class TradeGoodRegionModel {
    static async build({ goodId = "", locationId = "", sceneId = "", user = game.user } = {}) {
        const good = await TradeGoodsCatalog.getGood(goodId);
        const location = getLocation(locationId);
        if (!good || !location) return null;
        const resolvedSceneId = TradeRegionSceneService.resolveSceneId(location, { sceneId });
        const projection = TradeBasinEconomyService.getProjection(resolvedSceneId);
        const state = TradeBasinEconomyService.getMarketState(location, good.id, { sceneId: resolvedSceneId });
        const focusMarketplace = this.#marketplace(location.id);
        const focusRules = focusMarketplace ? SettlementMarketplaceModel.getRules(focusMarketplace, location) : null;
        const visible = candidate => user?.isGM || canUserViewLocationDetails(candidate.entityId, user);
        const regionalLocations = projection.locations.length ? projection.locations : [state.location].filter(Boolean);
        const producers = regionalLocations
            .filter(candidate => candidate.produces.includes(good.id))
            .filter(visible)
            .map(candidate => {
                const marketplace = this.#marketplace(candidate.entityId);
                const rules = marketplace ? SettlementMarketplaceModel.getRules(marketplace, getLocation(candidate.entityId)) : null;
                const center = projection.locationsByNodeId.get(candidate.centerNodeId) || null;
                return {
                    ...this.#locationView(candidate),
                    tradePositionLabel: center && visible(center) ? `Trades through ${center.name}` : "Trade Hub",
                    hasMarketplace: !!marketplace,
                    sourceRatePercent: rules ? Math.round(rules.regionalMultiplier * 100) : null
                };
            })
            .sort((left, right) => left.name.localeCompare(right.name));

        const buyerMarkets = projection.locations
            .filter(visible)
            .map(candidate => {
                const marketplace = this.#marketplace(candidate.entityId);
                const basin = projection.basinsByNodeId.get(candidate.nodeId);
                if (!marketplace || !basin || basin.goods.has(good.id)) return null;
                const rules = SettlementMarketplaceModel.getRules(marketplace, getLocation(candidate.entityId));
                if (!rules.buysTradeGoods) return null;
                return {
                    ...this.#locationView(candidate),
                    marketName: marketplace.name || `${candidate.name} Marketplace`,
                    ratePercent: Math.round(rules.importMultiplier * 100),
                    basinName: TradeBasinEconomyService.getMarketLabel(candidate.entityId, { user, sceneId: resolvedSceneId })
                };
            })
            .filter(Boolean)
            .sort((left, right) => Number(right.ratePercent) - Number(left.ratePercent)
                || Number(right.typeId === "city") - Number(left.typeId === "city")
                || left.name.localeCompare(right.name));

        const suppliedMarkets = projection.locations
            .filter(visible)
            .filter(candidate => {
                const marketplace = this.#marketplace(candidate.entityId);
                return marketplace && SettlementMarketplaceModel.getRules(marketplace, getLocation(candidate.entityId)).buysTradeGoods;
            })
            .filter(candidate => projection.basinsByNodeId.get(candidate.nodeId)?.goods.has(good.id));

        const route = this.#routeView({
            location: state.location,
            projection,
            visible,
            isLocal: state.isLocal,
            isRegionallySupplied: state.isRegionallySupplied
        });

        return {
            good: {
                id: good.id,
                name: good.name,
                imageSrc: good.imageSrc || "",
                category: good.familyId || "Trade Good",
                tradeValue: Number(good.tradeValue || 0),
                lotLabel: good.lot?.label || "Trade lot",
                description: good.lot?.description || ""
            },
            locationId: location.id,
            sceneId: resolvedSceneId,
            sceneName: TradeRegionSceneService.getSceneName(resolvedSceneId),
            hasRegion: !!resolvedSceneId,
            status: state.status,
            statusLabel: state.isLocal
                ? "Produced Here"
                : state.isRegionallySupplied
                    ? "Available Here"
                    : focusRules?.buysTradeGoods ? "Wanted Here" : "Not Available Here",
            route,
            hasRoute: !!route,
            producers,
            hasProducers: producers.length > 0,
            producerCount: producers.length,
            buyerMarkets,
            hasBuyerMarkets: buyerMarkets.length > 0,
            buyerMarketCount: buyerMarkets.length,
            suppliedMarketCount: suppliedMarkets.length,
            isGM: user?.isGM === true
        };
    }

    static #marketplace(locationId) {
        return getShopsForParentLocation(locationId).find(shop => SettlementMarketplaceModel.isMarketplace(shop)) || null;
    }

    static #locationView(location) {
        return {
            entityId: location.entityId,
            nodeId: location.nodeId,
            name: location.name,
            imageSrc: location.imageSrc,
            subtitle: location.subtitle,
            typeId: location.typeId
        };
    }

    static #routeView({ location = null, projection = null, visible = () => true, isLocal = false, isRegionallySupplied = false } = {}) {
        if (!location || !projection) return null;
        const routeLocations = [];
        const visited = new Set();
        let current = location;
        let incomplete = false;

        while (current && !visited.has(current.nodeId)) {
            visited.add(current.nodeId);
            if (current !== location && !visible(current)) {
                incomplete = true;
                break;
            }
            routeLocations.push(current);
            if (!current.centerNodeId) break;
            const next = projection.locationsByNodeId.get(current.centerNodeId) || null;
            if (!next || visited.has(next.nodeId)) {
                incomplete = true;
                break;
            }
            current = next;
        }

        if (!routeLocations.length) return null;
        const nodes = routeLocations.map((candidate, index) => {
            const isFocus = index === 0;
            const isTerminal = !candidate.centerNodeId;
            let roleLabel = "Collection Market";
            if (isFocus) {
                if (isTerminal) roleLabel = isLocal ? "Producer & Trade Hub" : "Trade Hub";
                else roleLabel = isLocal ? "Producer" : isRegionallySupplied ? "Available Here" : "Current Market";
            } else if (isTerminal) {
                roleLabel = "Trade Hub";
            }
            return {
                ...this.#locationView(candidate),
                roleLabel,
                isTerminal
            };
        });

        return {
            heading: isLocal ? `Route from ${location.name}` : `Trade Network at ${location.name}`,
            description: isLocal
                ? "Follow this good from its producer toward the network's Trade Hub."
                : "See how this market connects to its network's Trade Hub.",
            nodes,
            incomplete,
            isIndependent: (projection.basinsByNodeId.get(location.nodeId)?.members.length || 0) <= 1
        };
    }
}
