import { missionSet } from "../mission_set/mission-set";
import {
    PendingReroute,
    PendingWaypointRemoval,
    PendingWaypointRemovalProposal,
} from "./exclusion-zone-set";
import { detectReroutesWithOverrides, getBlockingZoneIDs } from "./exclusion-zone-router";
import { MAX_WAYPOINTS } from "../../utils/constants";

/**
 * Marks proposals whose rerouted plan would exceed MAX_WAYPOINTS as isOverLimit.
 * Over-limit proposals are kept so their involvedZoneIDs are available for
 * per-zone filtering. totalBypassCount reflects feasible proposals only.
 *
 * @param {PendingReroute} reroute Reroute object whose proposals need over-limit classification
 * @returns {PendingReroute} Updated reroute with isOverLimit set on proposals that exceed the waypoint limit
 */
function markOverLimit(reroute: PendingReroute): PendingReroute {
    const proposals = reroute.proposals.map((p) =>
        p.newWaypoints.length > MAX_WAYPOINTS ? { ...p, isOverLimit: true as const } : p,
    );
    const totalBypassCount = proposals
        .filter((p) => !p.isOverLimit)
        .reduce((sum, p) => sum + p.bypassCount, 0);
    return { ...reroute, proposals, totalBypassCount };
}

/**
 * Scans all missions for zone crossings and returns reroute proposals.
 * Returns null if no missions are affected.
 * Proposals exceeding MAX_WAYPOINTS are marked isOverLimit so callers can
 * decide how to handle them (e.g. remove the mission, skip the zone).
 *
 * @returns {PendingReroute | null} Reroute proposals for all affected missions, or null if none are affected
 */
export function detectMissionReroutes(): PendingReroute | null {
    const reroute = detectReroutesWithOverrides(new Map());
    if (!reroute) return null;
    return markOverLimit(reroute);
}

/**
 * Scans all missions for waypoints that fall inside the safety buffer of any
 * active exclusion zone. Works on clean (bypass-stripped) waypoints so that
 * auto-generated bypass waypoints are never flagged. Returns removal proposals,
 * or null if no missions are affected.
 *
 * @param {number} [triggeringZoneID] Zone that caused this check, stored on the result so callers can revert it on cancel
 * @param {boolean} [removeOffendingZonesOnCancel] When true, the offending zone IDs are stored so they can be removed if the operator cancels
 * @returns {PendingWaypointRemoval | null} Removal proposals for all affected missions, or null if none are affected
 */
export function detectWaypointRemovals(
    triggeringZoneID?: number,
    removeOffendingZonesOnCancel?: boolean,
): PendingWaypointRemoval | null {
    const proposals: PendingWaypointRemovalProposal[] = [];
    const offendingZoneIDSet = new Set<number>();

    for (const [missionID, mission] of missionSet.getMissions()) {
        const cleanWaypoints = mission.getWaypoints().filter((wp) => !wp.getIsBypass());

        const keepWaypoints = cleanWaypoints.filter((wp) => {
            const loc = wp.getLocation();
            if (!loc) return true;
            const blockingIDs = getBlockingZoneIDs(loc);
            if (blockingIDs.length === 0) return true;
            blockingIDs.forEach((id) => offendingZoneIDSet.add(id));
            return false;
        });

        const removedCount = cleanWaypoints.length - keepWaypoints.length;
        if (removedCount === 0) continue;

        proposals.push({ missionID, newWaypoints: keepWaypoints, removedCount });
    }

    if (proposals.length === 0) return null;

    // Pre-compute reroutes against the post-removal state so both can be shown
    // in a single dialog without a second round-trip through the data model.
    const overrides = new Map(proposals.map((p) => [p.missionID, p.newWaypoints]));
    const rawFollowUpReroute = detectReroutesWithOverrides(overrides);
    const followUpReroute = rawFollowUpReroute ? markOverLimit(rawFollowUpReroute) : undefined;

    const result: PendingWaypointRemoval = {
        proposals,
        totalRemovedCount: proposals.reduce((sum, p) => sum + p.removedCount, 0),
        followUpReroute,
    };

    if (triggeringZoneID !== undefined) {
        result.triggeringZoneID = triggeringZoneID;
    } else if (removeOffendingZonesOnCancel) {
        result.offendingZoneIDs = Array.from(offendingZoneIDSet);
    }

    return result;
}
