
import { DIRECTIONS } from './Constants.js';
import { GridSystem } from './GridSystem.js';
import { DungeonDeck } from './DungeonDeck.js';

const PLACEMENT_STATE = {
    WAITING_DOOR: 'WAITING_DOOR',
    PLACING_TILE: 'PLACING_TILE',
    COMPLETE: 'COMPLETE'
};

export class DungeonGenerator {
    constructor() {
        this.grid = new GridSystem();
        this.deck = new DungeonDeck();
        this.placedTiles = [];
        this.availableExits = []; // Exits where player can choose to expand

        // Placement State
        this.state = PLACEMENT_STATE.WAITING_DOOR;
        this.currentCard = null;
        this.placementRotation = DIRECTIONS.NORTH;
        this.placementX = 0;
        this.placementY = 0;
        this.selectedExit = null;

        // Callbacks for UI
        this.onStateChange = null;
        this.onPlacementUpdate = null;
        this.onDoorBlocked = null;
    }

    startDungeon(missionConfig) {
        const objectiveId = missionConfig?.type === 'quest' ? 'room_objective' : 'room_dungeon';
        this.deck.generateMissionDeck(objectiveId);

        // 1. Draw and place first card automatically at origin
        const firstCard = this.deck.draw();
        if (!firstCard) {
            console.error("❌ Empty deck");
            return;
        }

        this.placeCardFinal(firstCard, 0, 0, DIRECTIONS.NORTH);


        // 2. Transition to door selection
        this.state = PLACEMENT_STATE.WAITING_DOOR;
        this.notifyStateChange();
    }

    /**
     * Player selects a door to expand from
     */
    selectDoor(exitPoint) {


        if (this.state !== PLACEMENT_STATE.WAITING_DOOR) {
            console.warn("Not in door selection mode");
            return false;
        }

        if (!exitPoint) {
            console.error("exitPoint is undefined!");
            return false;
        }

        // Validate exit exists
        const exitExists = this.availableExits.some(
            e => e.x === exitPoint.x && e.y === exitPoint.y && e.direction === exitPoint.direction
        );

        if (!exitExists) {
            console.warn("Invalid exit selected");
            return false;
        }

        this.selectedExit = exitPoint;

        // Draw next card
        this.currentCard = this.deck.draw();
        if (!this.currentCard) {

            this.state = PLACEMENT_STATE.COMPLETE;
            this.notifyStateChange();
            return false;
        }

        // Calculate initial placement position (3 units above the connection point)
        const connectionPoint = this.neighbor(exitPoint.x, exitPoint.y, exitPoint.direction);

        // Start with NORTH rotation
        this.placementRotation = DIRECTIONS.NORTH;
        const variant = this.currentCard.variants[this.placementRotation];

        // Find the exit on the new tile that should connect to selectedExit
        const requiredDirection = this.opposite(exitPoint.direction);
        const matchingExits = variant.exits.filter(e => e.direction === requiredDirection);

        if (matchingExits.length > 0) {
            // Use first matching exit as anchor
            const anchor = matchingExits[0];
            this.placementX = Math.round(connectionPoint.x - anchor.x);
            this.placementY = Math.round(connectionPoint.y - anchor.y);
        } else {
            // Fallback: center on connection point
            this.placementX = Math.round(connectionPoint.x);
            this.placementY = Math.round(connectionPoint.y);
        }

        this.state = PLACEMENT_STATE.PLACING_TILE;
        this.notifyPlacementUpdate();
        this.notifyStateChange();


        return true;
    }

    /**
     * Rotate placement tile 90° clockwise
     */
    rotatePlacement() {
        if (this.state !== PLACEMENT_STATE.PLACING_TILE) return;

        const rotations = [DIRECTIONS.NORTH, DIRECTIONS.EAST, DIRECTIONS.SOUTH, DIRECTIONS.WEST];
        const currentIndex = rotations.indexOf(this.placementRotation);
        this.placementRotation = rotations[(currentIndex + 1) % 4];


        this.notifyPlacementUpdate();
    }

    /**
     * Move placement tile by offset
     */
    movePlacement(dx, dy) {
        if (this.state !== PLACEMENT_STATE.PLACING_TILE) return;

        this.placementX += dx;
        this.placementY += dy;


        this.notifyPlacementUpdate();
    }

    /**
     * Check if current placement is valid
     */
    isPlacementValid() {
        if (!this.currentCard || this.state !== PLACEMENT_STATE.PLACING_TILE) return false;

        const variant = this.currentCard.variants[this.placementRotation];
        return this.grid.canPlace(variant, this.placementX, this.placementY);
    }

    cancelPlacement() {
        if (this.state !== PLACEMENT_STATE.PLACING_TILE) return;

        // 1. Mark door as blocked visually
        if (this.onDoorBlocked && this.selectedExit) {
            this.onDoorBlocked(this.selectedExit);
        }

        // 2. Remove the selected exit from available exits
        if (this.selectedExit) {
            this.availableExits = this.availableExits.filter(e =>
                !(e.x === this.selectedExit.x && e.y === this.selectedExit.y && e.direction === this.selectedExit.direction)
            );
        }

        // 3. Reset state
        this.currentCard = null;
        this.selectedExit = null;
        this.state = PLACEMENT_STATE.WAITING_DOOR;

        this.notifyPlacementUpdate();
        this.notifyStateChange();
    }

    /**
     * Confirm and finalize tile placement
     */
    confirmPlacement() {
        if (this.state !== PLACEMENT_STATE.PLACING_TILE) {
            console.warn("Not in placement mode");
            return false;
        }

        if (!this.isPlacementValid()) {
            console.warn("❌ Cannot place tile - invalid position");
            return false;
        }



        // Round to integers (tiles must be on grid cells)
        const finalX = Math.round(this.placementX);
        const finalY = Math.round(this.placementY);



        // Place the tile
        this.placeCardFinal(
            this.currentCard,
            finalX,
            finalY,
            this.placementRotation
        );

        // Reset placement state
        this.currentCard = null;
        this.selectedExit = null;
        this.state = PLACEMENT_STATE.WAITING_DOOR;

        this.notifyPlacementUpdate(); // Clear preview
        this.notifyStateChange();

        return true;
    }

    /**
     * Internal: Actually place a card on the grid
     */
    placeCardFinal(card, x, y, rotation) {
        const variant = card.variants[rotation];

        const instance = {
            id: `tile_${this.placedTiles.length}`,
            defId: card.id,
            x, y, rotation,
            name: card.name
        };

        this.grid.placeTile(instance, variant, card);
        this.placedTiles.push(instance);

        // Update available exits
        this.updateAvailableExits(instance, variant, x, y);
    }

    /**
     * Update list of exits player can choose from
     */
    updateAvailableExits(instance, variant, anchorX, anchorY) {


        // Add new exits from this tile
        for (const ex of variant.exits) {
            const gx = anchorX + ex.x;
            const gy = anchorY + ex.y;

            const leadingTo = this.neighbor(gx, gy, ex.direction);
            const isOccupied = this.grid.isOccupied(leadingTo.x, leadingTo.y);



            if (!isOccupied) {
                this.availableExits.push({
                    x: gx,
                    y: gy,
                    direction: ex.direction,
                    tileId: instance.id
                });

            }
        }



        // Remove exits that are now blocked or connected
        this.availableExits = this.availableExits.filter(exit => {
            const leadingTo = this.neighbor(exit.x, exit.y, exit.direction);
            return !this.grid.isOccupied(leadingTo.x, leadingTo.y);
        });
    }

    /**
     * Get current placement preview data for renderer
     */
    getPlacementPreview() {
        if (this.state !== PLACEMENT_STATE.PLACING_TILE || !this.currentCard) {
            return null;
        }

        const variant = this.currentCard.variants[this.placementRotation];
        const cells = this.grid.calculateCells(variant, this.placementX, this.placementY);
        const isValid = this.isPlacementValid();

        return {
            card: this.currentCard,
            rotation: this.placementRotation,
            x: this.placementX,
            y: this.placementY,
            cells,
            isValid,
            variant
        };
    }

    // --- Helpers ---

    notifyStateChange() {
        if (this.onStateChange) {
            this.onStateChange(this.state);
        }
    }

    notifyPlacementUpdate() {
        if (this.onPlacementUpdate) {
            const preview = this.getPlacementPreview();
            this.onPlacementUpdate(preview);
        }
    }

    neighbor(x, y, dir) {
        switch (dir) {
            case DIRECTIONS.NORTH: return { x, y: y + 1 };
            case DIRECTIONS.SOUTH: return { x, y: y - 1 };
            case DIRECTIONS.EAST: return { x: x + 1, y };
            case DIRECTIONS.WEST: return { x: x - 1, y };
        }
    }

    opposite(dir) {
        const map = {
            [DIRECTIONS.NORTH]: DIRECTIONS.SOUTH,
            [DIRECTIONS.SOUTH]: DIRECTIONS.NORTH,
            [DIRECTIONS.EAST]: DIRECTIONS.WEST,
            [DIRECTIONS.WEST]: DIRECTIONS.EAST
        };
        return map[dir];
    }
}
