import { system, world } from "@minecraft/server";

// === CONSTANTS ===
const NIGHT_COLORS = [
    { color: "§c", name: "red night" },
    { color: "§b", name: "cyan night" },
    { color: "§7", name: "blackhole night" },
    { color: "§d", name: "pink night" },
    { color: "§7", name: "invisible night" }
];

const SEA_LEVEL = 63;
const IGNORE_BLOCKS = [
    "minecraft:leaves", "minecraft:leaves2", "minecraft:log", "minecraft:log2",
    "minecraft:grass", "minecraft:tallgrass", "minecraft:large_fern", "minecraft:fern",
    "minecraft:flower", "minecraft:air",
    "aria_ah:dark_leaves", "aria_ah:dark_log"
];
const SURFACE_BLOCKS = [
    "minecraft:grass_block", "minecraft:sand", "minecraft:gravel", "minecraft:dirt",
    "minecraft:podzol", "minecraft:mycelium", "minecraft:red_sand", "minecraft:snow",
    "minecraft:snow_block", "minecraft:moss_block", "minecraft:clay", "minecraft:mud",
    "minecraft:coarse_dirt", "minecraft:rooted_dirt", "minecraft:grass_path",
    "aria_ah:grass", "aria_ah:dirt", "minecraft:stone", "minecraft:grass", "minecraft:tallgrass"
];
const CAVE_BLOCKS = [
    "minecraft:stone",
    "minecraft:deepslate",
    "minecraft:cobbled_deepslate",
    "minecraft:calcite",
    "minecraft:tuff",
    "minecraft:andesite",
    "minecraft:diorite",
    "minecraft:granite",
    "minecraft:clay",
    "minecraft:gravel",
    "minecraft:dripstone_block",
    "minecraft:pointed_dripstone",
    "minecraft:moss_block",
    "minecraft:moss_carpet",
    "minecraft:azalea",
    "minecraft:flowering_azalea",
    "minecraft:sculk",
    "minecraft:sculk_vein",
    "minecraft:sculk_catalyst",
    "minecraft:packed_mud",
    "minecraft:smooth_basalt",
    "minecraft:magma_block"
];
const CREATURE_LIST = [
    { name: "cat",     type: "aria_ah:cat",     min: 1, max: 3 },
    { name: "chicken", type: "aria_ah:chicken", min: 1, max: 1 },
    { name: "pig",     type: "aria_ah:pig",     min: 1, max: 2 },
    { name: "sheep",   type: "aria_ah:sheep",   min: 1, max: 1 },
    { name: "cow",     type: "aria_ah:cow",     min: 1, max: 4 },
    { name: "fox",     type: "aria_ah:fox",     min: 1, max: 2 },
    { name: "horse",   type: "aria_ah:horse",   min: 1, max: 2 },
    { name: "rabbit",  type: "aria_ah:rabbit",  min: 1, max: 3 },
    { name: "wolf",    type: "aria_ah:wolf",    min: 1, max: 6 }
];

// === ENTITY VALIDATION ===
function isValidEntity(entity) {
    return entity && entity.isValid && entity.location;
}

// === DEBUG UTILITIES ===
function isDebugEnabled() {
    try {
        const scoreboard = world.scoreboard.getObjective("aria_ah.dev_mode");
        if (!scoreboard) return false;
        for (const p of scoreboard.getParticipants()) {
            if (p.displayName === "debug") {
                return scoreboard.getScore(p) === 1;
            }
        }
    } catch (e) {}
    return false;
}
function sendDebug(msg) {
    if (isDebugEnabled()) world.sendMessage(msg);
}

// === NIGHTFALL CHECK ===
function isNightfall() {
    try {
        const scoreboard = world.scoreboard.getObjective("aria_ah.general");
        if (!scoreboard) return false;
        for (const p of scoreboard.getParticipants()) {
            if (p.displayName === "nightfall") {
                return scoreboard.getScore(p) === 1;
            }
        }
    } catch (e) {}
    return false;
}

// === SCOREBOARD HELPERS ===
function getScore(scoreboardName, participant, defaultValue = 0) {
    try {
        const scoreboard = world.scoreboard.getObjective(scoreboardName);
        if (!scoreboard) return defaultValue;
        for (const p of scoreboard.getParticipants()) {
            if (p.displayName === participant) return scoreboard.getScore(p);
        }
    } catch (e) {}
    return defaultValue;
}

// === PLAYER PROXIMITY CHECK ===
function isPlayerNearby(location, radius = 100) {
    if (!location) return false;
    const players = world.getAllPlayers();
    const radiusSquared = radius * radius;
    for (const player of players) {
        if (!isValidEntity(player)) continue;
        const dx = player.location.x - location.x;
        const dz = player.location.z - location.z;
        if (dx*dx + dz*dz <= radiusSquared) return true;
    }
    return false;
}
function getNearestPlayerDistanceSquared(location) {
    let minDistSq = Infinity;
    for (const player of world.getAllPlayers()) {
        if (!isValidEntity(player)) continue;
        const dx = player.location.x - location.x;
        const dz = player.location.z - location.z;
        const distSq = dx*dx + dz*dz;
        if (distSq < minDistSq) minDistSq = distSq;
    }
    return minDistSq;
}
function getFurthestEntity(entities) {
    let maxDistSq = -1;
    let furthest = null;
    for (const entity of entities) {
        if (!isValidEntity(entity)) continue;
        const distSq = getNearestPlayerDistanceSquared(entity.location);
        if (distSq > maxDistSq) {
            maxDistSq = distSq;
            furthest = entity;
        }
    }
    return { entity: furthest, distSq: maxDistSq };
}

// === CAVE DETECTION (accepts location and dimension) ===
function canSpawnCave(location, dimension) {
    if (location.y >= SEA_LEVEL) {
        sendDebug("§c[Cave] Too high (above sea level)");
        return false;
    }
    for (let y = Math.ceil(location.y) + 1; y <= 320; y++) {
        const block = dimension.getBlock({ 
            x: Math.floor(location.x), 
            y: y, 
            z: Math.floor(location.z) 
        });
        if (block && !block.isAir && !IGNORE_BLOCKS.includes(block.typeId)) {
            return true;
        }
    }
    return false;
}

// === HORROR CREATURE PROXIMITY ===
function isFarFromHorrorCreatures(dimension, location, minDist = 15) {
    const horrorMobs = dimension.getEntities({ families: ["aria_ah_horror_creature"] });
    return !horrorMobs.some(e => {
        const dx = e.location.x - location.x;
        const dz = e.location.z - location.z;
        return Math.sqrt(dx*dx + dz*dz) < minDist;
    });
}

// === WEIGHTED PICKERS ===
function buildWeightedList(weightGetter) {
    return CREATURE_LIST.map(c => ({
        ...c,
        weight: weightGetter(c.name)
    })).filter(c => c.weight > 0);
}

function pickWeightedCreature(weightedList) {
    if (weightedList.length === 0) return null;
    const totalWeight = weightedList.reduce((sum, c) => sum + c.weight, 0);
    let r = Math.random() * totalWeight;
    for (const creature of weightedList) {
        if (r < creature.weight) return creature;
        r -= creature.weight;
    }
    return weightedList[0];
}

// === ENTITY MANAGEMENT ===
function getEntitiesWithFamily(dimension, family) {
    return Array.from(dimension.getEntities({ families: [family] })).filter(isValidEntity);
}

// === FIND VALID SPAWN SPOT (Cave: 1x1x2 air, valid floor, roof above) ===
function findUnobstructedSpot(dimension, center, radius, isCave) {
    const validBlocks = isCave ? CAVE_BLOCKS : SURFACE_BLOCKS;
    for (let i = 0; i < 20; i++) {
        const angle = Math.random() * Math.PI * 2;
        const dist = Math.random() * radius;
        const x = Math.floor(center.x + Math.cos(angle) * dist);
        const z = Math.floor(center.z + Math.sin(angle) * dist);
        const y = Math.floor(center.y);
        const floorBlock = dimension.getBlock({ x, y: y - 1, z });
        const block = dimension.getBlock({ x, y, z });
        const blockAbove = dimension.getBlock({ x, y: y + 1, z });

        if (!validBlocks.includes(floorBlock?.typeId)) {
            sendDebug(`§cInvalid ${isCave ? "cave" : "surface"} floor: ${floorBlock?.typeId}`);
            continue;
        }
        if (block.isAir && blockAbove.isAir) {
            return { x, y, z };
        }
    }
    return null;
}

// === FOG MANAGEMENT ===
function handleFogSpawning(dummy, fogAmount, fogSpawnChance, prefix, isCave) {
    if (!isValidEntity(dummy)) return;
    const fogEntities = dummy.dimension.getEntities({ type: "aria_ah:dark_fog" })
        .filter(fog => {
            if (!isValidEntity(fog)) {
                fog.remove();
                return false;
            }
            return true;
        });
    fogEntities.filter(fog => getNearestPlayerDistanceSquared(fog.location) > 10000)
        .forEach(fog => {
            fog.remove();
            sendDebug(`§8[FOG] Despawned distant fog`);
        });
    if (fogEntities.length >= fogAmount) return;
    if (Math.random() < fogSpawnChance && isPlayerNearby(dummy.location)) {
        if (isCave && !canSpawnCave(dummy.location, dummy.dimension)) {
            sendDebug("§6[INFO] Cave fog can only spawn in caves below sea level.");
            return;
        }
        try {
            dummy.dimension.spawnEntity("aria_ah:dark_fog", {
                x: dummy.location.x,
                y: dummy.location.y + 1,
                z: dummy.location.z
            });
            sendDebug(`§aSpawned fog (${fogEntities.length + 1}/${fogAmount})`);
        } catch (e) {
            sendDebug("§cFailed to spawn fog");
        }
    }
}

// === CREATURE MANAGEMENT (with cave logic) ===
function doCreatureSpawns(dummy, weightedList, creatureAmount, spawnTag, nightLabel, isCave, nightPrefix) {
    if (!isValidEntity(dummy)) {
        sendDebug("§c[CREATURE] Invalid dummy entity");
        return;
    }
    const dimension = dummy.dimension;
    let allCreatures = getEntitiesWithFamily(dimension, "aria_ah_creature");
    // Remove invalid creatures
    allCreatures = allCreatures.filter(creature => {
        if (!isValidEntity(creature)) {
            sendDebug(`§8[${nightLabel}] Removing invalid ${creature.typeId}`);
            creature.remove();
            return false;
        }
        return true;
    });
    // Remove distant creatures
    const distantCreatures = allCreatures.filter(creature => {
        const distSq = getNearestPlayerDistanceSquared(creature.location);
        return distSq > 10000;
    });
    distantCreatures.forEach(creature => {
        creature.remove();
        sendDebug(`§8[${nightLabel}] Despawned distant ${creature.typeId.replace("aria_ah:", "")}`);
    });
    allCreatures = allCreatures.filter(c => !distantCreatures.includes(c));
    sendDebug(`§3${nightLabel.toUpperCase()} CREATURES: [${allCreatures.length}/${creatureAmount}]`);
    if (weightedList.length > 0) {
        sendDebug("§3Active Creature Weights:");
        weightedList.forEach(c => {
            sendDebug(` §7- ${c.name}: ${c.weight} (${c.min}-${c.max})`);
        });
    } else {
        sendDebug("§cNo valid creature weights configured!");
    }
    if (allCreatures.length >= creatureAmount) {
        const doubleLimit = creatureAmount * 2;
        const extras = allCreatures.length - creatureAmount;
        if (extras > 0) {
            allCreatures.slice(0, extras).forEach(e => e.remove());
            sendDebug(`§cDoubled limit to ${doubleLimit}, removed ${extras} excess`);
        }
        try {
            world.scoreboard.getObjective("aria_ah.spawn_rules")
                .setScore({ displayName: `${nightPrefix}_creature_amount` }, doubleLimit);
        } catch (e) {}
        return;
    }
    if (!isPlayerNearby(dummy.location)) {
        sendDebug(`§7[${nightLabel}] No players nearby - skipping creature spawns`);
        return;
    }
    if (!isFarFromHorrorCreatures(dimension, dummy.location, 15)) {
        sendDebug("§6[INFO] Too close to another horror creature. Herd spawn cancelled.");
        return;
    }
    const creature = pickWeightedCreature(weightedList);
    if (!creature) return;
    const herdSize = creature.min + Math.floor(Math.random() * (creature.max - creature.min + 1));
    sendDebug(`§3Attempting to spawn ${herdSize} ${creature.name}`);
    let spawned = 0;
    for (let i = 0; i < herdSize && (allCreatures.length + spawned) < creatureAmount; i++) {
        const spot = findUnobstructedSpot(dimension, dummy.location, 5, isCave);
        if (!spot) {
            sendDebug(`§cNo valid spot found in 20 attempts`);
            continue;
        }
        // CAVE LOGIC: Y-level, roof, and surface block
        if (isCave) {
            if (spot.y >= SEA_LEVEL) {
                sendDebug(`§6[${nightLabel}] Cave spawn failed: Above sea level`);
                continue;
            }
            if (!canSpawnCave(spot, dimension)) {
                sendDebug(`§6[${nightLabel}] Cave spawn failed: No solid roof above`);
                continue;
            }
            const floorBlock = dimension.getBlock({ x: spot.x, y: spot.y - 1, z: spot.z });
            if (!CAVE_BLOCKS.includes(floorBlock?.typeId)) {
                sendDebug(`§6[${nightLabel}] Cave spawn failed: Invalid floor (${floorBlock?.typeId})`);
                continue;
            }
        }
        if (isPlayerNearby(spot)) {
            try {
                const entity = dimension.spawnEntity(creature.type, spot);
                if (entity) entity.addTag(spawnTag);
                spawned++;
                sendDebug(`§aSpawned ${creature.type} at ${spot.x},${spot.y},${spot.z}`);
            } catch (e) {
                sendDebug(`§cFailed to spawn ${creature.type}: ${e.message}`);
            }
        }
    }
    if (spawned > 0) {
        sendDebug(`§aSpawned ${spawned} ${creature.type.replace("aria_ah:", "")}${spawned > 1 ? "s" : ""} (herd, ${nightLabel}, weight ${creature.weight})`);
    }
}

// === NIGHT TYPE WEIGHT GETTERS ===
function getBloodCreatureWeightScore(creatureName) {
    return getScore("aria_ah.spawn_rules", `blood_${creatureName}_weight`);
}
function getBlackholeCreatureWeightScore(creatureName) {
    return getScore("aria_ah.spawn_rules", `blackhole_${creatureName}_weight`);
}
function getCyanCreatureWeightScore(creatureName) {
    return getScore("aria_ah.spawn_rules", `cyan_${creatureName}_weight`);
}
function getCaveCreatureWeightScore(creatureName) {
    return getScore("aria_ah.spawn_rules", `cave_${creatureName}_weight`);
}

// === MAIN EVENT HANDLER ===
system.afterEvents.scriptEventReceive.subscribe((event) => {
    if (event.id !== "aria_ah:spawn_rule_update") return;
    const dummy = event.sourceEntity;
    if (!isValidEntity(dummy) || dummy.dimension.id !== "minecraft:overworld") {
        if (dummy?.isValid) dummy.remove();
        return;
    }
    if (!isNightfall()) {
        sendDebug("§7[INFO] Not nightfall (nightfall!=1), no spawns.");
        dummy.remove();
        return;
    }
    const nightType = getScore("aria_ah.night", "type", -1);
    let nightMsg = "§8[DEBUG] Unknown night type.";
    if (nightType !== null && nightType >= 0 && nightType < NIGHT_COLORS.length) {
        const nightInfo = NIGHT_COLORS[nightType];
        nightMsg = `${nightInfo.color}[NIGHT] ${nightInfo.name}`;
    }
    sendDebug(nightMsg);

    // === Pink Night: No Spawns ===
    if (nightType === 3) {
        sendDebug("§dPink night is friendly! No spawns tonight.");
        dummy.remove();
        return;
    }

    // === Blood Night (Type 0) ===
    if (nightType === 0) {
        handleFogSpawning(
            dummy,
            getScore("aria_ah.spawn_rules", "blood_fog_amount", 2),
            getScore("aria_ah.spawn_rules", "blood_fog_spawn_chance", 50) / 100,
            "blood",
            false
        );
        const creatureChance = getScore("aria_ah.spawn_rules", "blood_creature_spawn_chance", 100) / 100;
        if (Math.random() < creatureChance) {
            const weightedList = buildWeightedList(getBloodCreatureWeightScore);
            doCreatureSpawns(
                dummy,
                weightedList,
                getScore("aria_ah.spawn_rules", "blood_creature_amount", 15),
                "aria_ah_blood_spawned",
                "blood night",
                false,
                "blood"
            );
        } else {
            sendDebug(`§7[Blood] Creature spawn chance failed (${(creatureChance * 100).toFixed(1)}%)`);
        }
        dummy.remove();
        return;
    }

    // === Cyan Night (Type 1) ===
    if (nightType === 1) {
        const creatureChance = getScore("aria_ah.spawn_rules", "cyan_creature_spawn_chance", 100) / 100;
        if (Math.random() < creatureChance) {
            const weightedList = buildWeightedList(getCyanCreatureWeightScore);
            doCreatureSpawns(
                dummy,
                weightedList,
                getScore("aria_ah.spawn_rules", "cyan_creature_amount", 25),
                "aria_ah_cyan_spawned",
                "cyan night",
                false,
                "cyan"
            );
        } else {
            sendDebug(`§7[Cyan] Creature spawn chance failed (${(creatureChance * 100).toFixed(1)}%)`);
        }
        dummy.remove();
        return;
    }

    // === Blackhole Night (Type 2) ===
    if (nightType === 2) {
        handleFogSpawning(
            dummy,
            getScore("aria_ah.spawn_rules", "blackhole_fog_amount", 2),
            getScore("aria_ah.spawn_rules", "blackhole_fog_spawn_chance", 50) / 100,
            "blackhole",
            false
        );
        const creatureChance = getScore("aria_ah.spawn_rules", "blackhole_creature_spawn_chance", 100) / 100;
        if (Math.random() < creatureChance) {
            const weightedList = buildWeightedList(getBlackholeCreatureWeightScore);
            doCreatureSpawns(
                dummy,
                weightedList,
                getScore("aria_ah.spawn_rules", "blackhole_creature_amount", 15),
                "aria_ah_black_spawned",
                "blackhole night",
                false,
                "blackhole"
            );
        } else {
            sendDebug(`§7[Blackhole] Creature spawn chance failed (${(creatureChance * 100).toFixed(1)}%)`);
        }
        dummy.remove();
        return;
    }

    // === Invisible Night (Type 4, Cave) ===
    if (nightType === 4) {
        if (!canSpawnCave(dummy.location, dummy.dimension)) {
            sendDebug("§6[INFO] Cave fog and creatures can only spawn in caves below sea level.");
            dummy.remove();
            return;
        }
        handleFogSpawning(
            dummy,
            getScore("aria_ah.spawn_rules", "cave_fog_amount", 3),
            getScore("aria_ah.spawn_rules", "cave_fog_spawn_chance", 50) / 100,
            "cave",
            true
        );
        const creatureChance = getScore("aria_ah.spawn_rules", "cave_creature_spawn_chance", 100) / 100;
        if (Math.random() < creatureChance) {
            const weightedList = buildWeightedList(getCaveCreatureWeightScore);
            doCreatureSpawns(
                dummy,
                weightedList,
                getScore("aria_ah.spawn_rules", "cave_creature_amount", 20),
                "aria_ah_cave_spawned",
                "cave night",
                true,
                "cave"
            );
        } else {
            sendDebug(`§7[Cave] Creature spawn chance failed (${(creatureChance * 100).toFixed(1)}%)`);
        }
        dummy.remove();
        return;
    }

    dummy.remove();
});
