import { world, system } from '@minecraft/server';
/**
 * Time tracking system for parkour runs
 *
 * HOW IT WORKS (Fully Automated):
 * 1. Timer AUTOMATICALLY starts when a player enters ANY grid (the first grid they enter)
 * 2. System continuously checks (every 1 second) if players are inside grids
 * 3. When player moves from one grid to another, it records the segment time
 * 4. If player leaves a grid without entering another grid, timer is CANCELLED
 * 5. When player enters a "finishing area" grid, the run completes and saves to leaderboard
 *
 * NO MANUAL TRIGGERS NEEDED - completely automatic based on player position in grids
 */
export class TimeTracker {
    activeSessions = new Map();
    areaData;
    objectiveName = "time_tracker_db";
    leaderboardName = "leaderboard_db";
    checkInterval = 20; // Check every 20 ticks (1 second)
    constructor(areaData) {
        this.areaData = areaData;
        this.initializeScoreboards();
        this.startPlayerTracking();
    }
    /**
     * Initialize scoreboard objectives for time tracking
     */
    initializeScoreboards() {
        system.run(() => {
            try {
                // Create time tracker database
                let objective = world.scoreboard.getObjective(this.objectiveName);
                if (!objective) {
                    world.scoreboard.addObjective(this.objectiveName, "Time Tracker DB");
                }
                // Create leaderboard database
                objective = world.scoreboard.getObjective(this.leaderboardName);
                if (!objective) {
                    world.scoreboard.addObjective(this.leaderboardName, "Leaderboard DB");
                }
            }
            catch (error) {
                // Failed to initialize scoreboards
            }
        });
    }
    /**
     * Start tracking player positions and area transitions
     * This runs automatically every 20 ticks (1 second) and continuously checks
     * if players are inside grids to handle timing
     */
    startPlayerTracking() {
        system.runInterval(() => {
            const players = world.getAllPlayers();
            for (const player of players) {
                // Continuously check each player's position
                this.checkPlayerPosition(player);
                // Update action bar timer for active sessions
                this.updateActionBar(player);
            }
        }, this.checkInterval);
    }
    /**
     * Update action bar with current segment timer
     */
    updateActionBar(player) {
        const session = this.activeSessions.get(player.id);
        if (!session || !session.isActive) {
            return;
        }
        const now = Date.now();
        // Calculate current segment time
        const segmentStartTime = session.segmentTimes.length > 0
            ? session.segmentTimes[session.segmentTimes.length - 1].endTime
            : session.startTime;
        const currentSegmentTime = now - segmentStartTime;
        const totalTime = now - session.startTime;
        // Get current area name synchronously
        const currentArea = this.getAreaByIdSync(session.currentAreaId);
        const areaName = currentArea ? currentArea.data.name : "Unknown Area";
        // Format times
        const segmentFormatted = this.formatTimeCompact(currentSegmentTime);
        const totalFormatted = this.formatTimeCompact(totalTime);
        // Display on action bar
        player.onScreenDisplay.setActionBar(`§eSegment: §f${segmentFormatted} §8| §aTotal: §f${totalFormatted}`);
    }
    /**
     * Get area by ID synchronously (directly from scoreboard without system.run)
     */
    getAreaByIdSync(areaId) {
        try {
            // Use the AreaData class to get area by ID
            return this.areaData.get(areaId);
        }
        catch (error) {
            return null;
        }
    }
    /**
     * Check player's current position and handle area transitions
     * Also checks for gold block finish trigger
     */
    checkPlayerPosition(player) {
        const currentArea = this.areaData.getAreaByPosition(player.location);
        const session = this.activeSessions.get(player.id);
        // Check if player is standing on a gold block in the finish area
        if (currentArea && currentArea.data.isFinishingArea && session && session.isActive) {
            try {
                const blockBelow = player.dimension.getBlock({
                    x: Math.floor(player.location.x),
                    y: Math.floor(player.location.y) - 1,
                    z: Math.floor(player.location.z)
                });
                if (blockBelow && blockBelow.typeId === 'minecraft:gold_block') {
                    this.completeRun(player, session);
                    return;
                }
            }
            catch (error) {
                // Error checking block
            }
        }
        // Player is not in any area (gap between zones or completely outside)
        if (!currentArea) {
            // Timer keeps running in the background (allow gaps)
            return;
        }
        // Player entered an area for the first time (no active session)
        if (!session) {
            // Start timer automatically when entering ANY grid (first grid = starting point)
            this.startRun(player, currentArea);
        }
        else if (session.isActive && session.currentAreaId !== currentArea.areaId) {
            // Player moved to a different area - record segment time
            this.handleAreaTransition(player, session, currentArea);
        }
        // If player is still in the same area, do nothing (keep timer running)
    }
    /**
     * Start a new run when player enters a starting area
     */
    startRun(player, startArea) {
        const now = Date.now();
        const session = {
            playerId: player.id,
            playerName: player.name,
            startTime: now,
            currentAreaId: startArea.areaId,
            lastAreaId: null,
            segmentTimes: [],
            isActive: true,
            hasReachedFinish: false
        };
        this.activeSessions.set(player.id, session);
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.sendMessage(`§a§l⏱ RUN STARTED!`);
        ;
        player.sendMessage(`§eTimer is now tracking your progress!`);
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.playSound("random.orb");
    }
    /**
     * Handle player transitioning from one area to another
     */
    handleAreaTransition(player, session, newArea) {
        const now = Date.now();
        // Get the previous area data directly from scoreboard (sync)
        const previousArea = this.getAreaByIdSync(session.currentAreaId);
        if (!previousArea) {
            return;
        }
        // Calculate segment time
        const segmentStartTime = session.segmentTimes.length > 0
            ? session.segmentTimes[session.segmentTimes.length - 1].endTime
            : session.startTime;
        const segmentTime = {
            fromAreaId: session.currentAreaId,
            fromAreaName: previousArea.data.name,
            toAreaId: newArea.areaId,
            toAreaName: newArea.data.name,
            startTime: segmentStartTime,
            endTime: now,
            duration: now - segmentStartTime
        };
        session.segmentTimes.push(segmentTime);
        session.lastAreaId = session.currentAreaId;
        session.currentAreaId = newArea.areaId;
        // Display segment completion message to player
        const segmentTimeFormatted = this.formatTime(segmentTime.duration);
        const segmentNumber = session.segmentTimes.length;
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.sendMessage(`§a§l✓ AREA COMPLETED!`);
        player.sendMessage(`§7Segment #${segmentNumber} Time: §f§l${segmentTimeFormatted}`);
        // Calculate total time so far
        const totalTime = now - session.startTime;
        const totalFormatted = this.formatTimeCompact(totalTime);
        player.sendMessage(`§7Total Time: §f${totalFormatted}`);
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.sendMessage(`§bNow entering: §f${newArea.data.name}`);
        player.playSound("random.levelup");
        // Don't auto-complete on area transition - only gold block triggers finish
    }
    /**
     * Complete a run when player reaches finishing area
     */
    completeRun(player, session) {
        const now = Date.now();
        const totalDuration = now - session.startTime;
        const completedRun = {
            runId: `run_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
            playerId: session.playerId,
            playerName: session.playerName,
            startTime: session.startTime,
            endTime: now,
            totalDuration: totalDuration,
            segmentTimes: session.segmentTimes,
            completedAt: now
        };
        // Save to database
        this.saveCompletedRun(completedRun);
        // Update leaderboard
        this.updateLeaderboard(completedRun);
        // Display completion message
        const totalTimeFormatted = this.formatTime(totalDuration);
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.sendMessage(`§a§l✓ RUN COMPLETED!`);
        player.sendMessage(`§6Total Time: §f§l${totalTimeFormatted}`);
        player.sendMessage(`§7Segments completed: §f${session.segmentTimes.length}`);
        // Show segment breakdown
        player.sendMessage(`§e\nSegment Breakdown:`);
        for (let i = 0; i < session.segmentTimes.length; i++) {
            const segment = session.segmentTimes[i];
            const segmentFormatted = this.formatTime(segment.duration);
            player.sendMessage(`§7${i + 1}. ${segment.fromAreaName} → ${segment.toAreaName}: §f${segmentFormatted}`);
        }
        player.sendMessage(`§6§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.playSound("random.levelup", { pitch: 1.5, volume: 1.0 });
        // Check if it's a personal best or leaderboard position
        const rank = this.getPlayerRank(player.id);
        if (rank) {
            player.sendMessage(`§6Your Rank: §f#${rank}`);
        }
        // Clear the active session
        this.activeSessions.delete(player.id);
        // Clear action bar
        player.onScreenDisplay.setActionBar(``);
        // Teleport player to spawn area (same as Exit Run)
        try {
            player.teleport({ x: -5, y: -23, z: 20 }, { dimension: player.dimension, rotation: { x: 0, y: -90 } });
            player.sendMessage("§aTeleported to spawn!");
        }
        catch (error) {
            // Teleport failed
        }
    }
    /**
     * Cancel a run when player leaves the course
     */
    cancelRun(player) {
        const session = this.activeSessions.get(player.id);
        if (!session)
            return;
        // Calculate how long they ran before cancelling
        const elapsed = Date.now() - session.startTime;
        const elapsedFormatted = this.formatTimeCompact(elapsed);
        session.isActive = false;
        this.activeSessions.delete(player.id);
        player.sendMessage(`§c§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.sendMessage(`§c§l⏱ RUN CANCELLED!`);
        player.sendMessage(`§7You left the Parkour area.`);
        player.sendMessage(`§7Time before cancel: §f${elapsedFormatted}`);
        player.sendMessage(`§7Segments completed: §f${session.segmentTimes.length}`);
        player.sendMessage(`§c§l━━━━━━━━━━━━━━━━━━━━━━━━`);
        player.playSound("random.break");
        // Clear action bar
        player.onScreenDisplay.setActionBar(``);
    }
    /**
     * Save completed run to database
     */
    saveCompletedRun(run) {
        system.run(() => {
            try {
                const objective = world.scoreboard.getObjective(this.objectiveName);
                if (!objective) {
                    return;
                }
                const serializedData = JSON.stringify(run);
                objective.setScore(serializedData, run.totalDuration);
            }
            catch (error) {
                // Failed to save run
            }
        });
    }
    /**
     * Update leaderboard with new run
     */
    updateLeaderboard(run) {
        system.run(() => {
            try {
                const objective = world.scoreboard.getObjective(this.leaderboardName);
                if (!objective) {
                    return;
                }
                // Get player's best time
                const playerBest = this.getPlayerBestTime(run.playerId);
                // Only update if this is a new personal best (or first time)
                if (!playerBest || run.totalDuration < playerBest.totalTime) {
                    const leaderboardEntry = {
                        playerId: run.playerId,
                        playerName: run.playerName,
                        totalTime: run.totalDuration,
                        completedAt: run.completedAt,
                        runId: run.runId
                    };
                    const serializedData = JSON.stringify(leaderboardEntry);
                    // Remove old entry if exists
                    if (playerBest) {
                        const participants = objective.getParticipants();
                        for (const participant of participants) {
                            try {
                                const entry = JSON.parse(participant.displayName);
                                if (entry.playerId === run.playerId) {
                                    objective.removeParticipant(participant.displayName);
                                    break;
                                }
                            }
                            catch {
                                continue;
                            }
                        }
                    }
                    // Add new entry
                    objective.setScore(serializedData, run.totalDuration);
                }
            }
            catch (error) {
                // Failed to update leaderboard
            }
        });
    }
    /**
     * Get player's best time from leaderboard
     */
    getPlayerBestTime(playerId) {
        try {
            const objective = world.scoreboard.getObjective(this.leaderboardName);
            if (!objective)
                return null;
            const participants = objective.getParticipants();
            for (const participant of participants) {
                try {
                    const entry = JSON.parse(participant.displayName);
                    if (entry.playerId === playerId) {
                        return {
                            rank: 0, // Will be calculated when needed
                            playerId: entry.playerId,
                            playerName: entry.playerName,
                            totalTime: entry.totalTime,
                            completedAt: entry.completedAt,
                            runId: entry.runId
                        };
                    }
                }
                catch {
                    continue;
                }
            }
        }
        catch (error) {
            // Failed to get player best time
        }
        return null;
    }
    /**
     * Get player's rank on leaderboard
     */
    getPlayerRank(playerId) {
        const leaderboard = this.getLeaderboard();
        for (let i = 0; i < leaderboard.length; i++) {
            if (leaderboard[i].playerId === playerId) {
                return i + 1;
            }
        }
        return null;
    }
    /**
     * Get full leaderboard sorted by time
     */
    getLeaderboard() {
        const entries = [];
        try {
            const objective = world.scoreboard.getObjective(this.leaderboardName);
            if (!objective)
                return entries;
            const participants = objective.getParticipants();
            for (const participant of participants) {
                try {
                    const entry = JSON.parse(participant.displayName);
                    entries.push({
                        rank: 0, // Will be set after sorting
                        playerId: entry.playerId,
                        playerName: entry.playerName,
                        totalTime: entry.totalTime,
                        completedAt: entry.completedAt,
                        runId: entry.runId
                    });
                }
                catch {
                    continue;
                }
            }
            // Sort by total time (ascending)
            entries.sort((a, b) => a.totalTime - b.totalTime);
            // Set ranks
            for (let i = 0; i < entries.length; i++) {
                entries[i].rank = i + 1;
            }
        }
        catch (error) {
            // Failed to get leaderboard
        }
        return entries;
    }
    /**
     * Get all runs for a specific player
     */
    getPlayerRuns(playerId) {
        const runs = [];
        try {
            const objective = world.scoreboard.getObjective(this.objectiveName);
            if (!objective)
                return runs;
            const participants = objective.getParticipants();
            for (const participant of participants) {
                try {
                    const run = JSON.parse(participant.displayName);
                    if (run.playerId === playerId) {
                        runs.push(run);
                    }
                }
                catch {
                    continue;
                }
            }
            // Sort by completion time (most recent first)
            runs.sort((a, b) => b.completedAt - a.completedAt);
        }
        catch (error) {
            // Failed to get player runs
        }
        return runs;
    }
    /**
     * Get current active session for a player
     */
    getActiveSession(player) {
        return this.activeSessions.get(player.id) || null;
    }
    /**
     * Format milliseconds to readable time string (MM:SS.mmm)
     */
    formatTime(milliseconds) {
        const totalSeconds = Math.floor(milliseconds / 1000);
        const minutes = Math.floor(totalSeconds / 60);
        const seconds = totalSeconds % 60;
        const ms = milliseconds % 1000;
        return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}`;
    }
    /**
     * Format time to compact format (MM:SS)
     */
    formatTimeCompact(milliseconds) {
        const totalSeconds = Math.floor(milliseconds / 1000);
        const minutes = Math.floor(totalSeconds / 60);
        const seconds = totalSeconds % 60;
        return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
    }
    /**
     * Manually cancel a player's run (admin command)
     */
    cancelPlayerRun(player) {
        if (this.activeSessions.has(player.id)) {
            this.cancelRun(player);
            return true;
        }
        return false;
    }
    /**
     * Check if a player has an active run
     */
    hasActiveRun(playerId) {
        return this.activeSessions.has(playerId);
    }
    /**
     * Get current area data for a player's active run
     */
    getCurrentArea(playerId) {
        const session = this.activeSessions.get(playerId);
        if (!session)
            return null;
        return this.getAreaByIdSync(session.currentAreaId);
    }
    /**
     * End a player's run without completing it
     */
    endRun(playerId) {
        const session = this.activeSessions.get(playerId);
        if (!session)
            return;
        const player = world.getAllPlayers().find(p => p.id === playerId);
        if (player) {
            this.cancelRun(player);
        }
    }
    /**
     * Clear all active runs (used for reset)
     */
    clearAllRuns() {
        this.activeSessions.clear();
    }
}
