/**
 * Developer: Molla Samser
 * Organization: RSK World
 * Website: https://rskworld.in
 * Email: help@rskworld.in, support@rskworld.in
 * Phone/WhatsApp: +91 93305 39277
 * Address: Nutanhat, Mongolkote, Purba Burdwan, West Bengal, India, 713147
 * Copyright © 2026 RSK World. All rights reserved.
 */
document.addEventListener('DOMContentLoaded', () => {
    // Game state
    let towers = [[], [], []];
    let selectedTower = null;
    let moveCount = 0;
    let diskCount = 3; // Default number of disks
    let isAnimating = false;
    let hasStarted = false;
    let timerInterval = null;
    let startTimeMs = 0;
    let elapsedMs = 0;
    let isSolving = false;
    let undoStack = [];
    let redoStack = [];
    let isMuted = false;

    // DOM Elements
    const towersEl = Array.from(document.querySelectorAll('.tower'));
    const moveCounterEl = document.getElementById('move-counter');
    const timerEl = document.getElementById('timer');
    const diskCountSelect = document.getElementById('disk-count');
    const newGameBtn = document.getElementById('new-game');
    const playAgainBtn = document.getElementById('play-again');
    const winModal = document.getElementById('win-message');
    const finalMovesEl = document.getElementById('final-moves');
    const finalTimeEl = document.getElementById('final-time');
    const minimalMovesEl = document.getElementById('minimal-moves');
    const nextLevelBtn = document.getElementById('next-level');
    const difficultyLabelEl = document.getElementById('difficulty-label');
    const ratingStarsEl = document.getElementById('rating-stars');
    const bestMovesEl = document.getElementById('best-moves');
    const bestTimeEl = document.getElementById('best-time');
    const historyToggleBtn = document.getElementById('history-toggle');
    const historyPanelEl = document.getElementById('history-panel');
    const moveHistoryEl = document.getElementById('move-history');
    const undoBtn = document.getElementById('undo-btn');
    const redoBtn = document.getElementById('redo-btn');
    const muteBtn = document.getElementById('mute-btn');
    const solveBtn = document.getElementById('solve-btn');
    const ariaLiveEl = document.getElementById('aria-live');
    const pauseBtn = document.getElementById('pause-btn');
    const pauseOverlay = document.getElementById('pause-overlay');
    const hintBtn = document.getElementById('hint-btn');
    const continueBtn = document.getElementById('continue-game');
    const goProBtn = document.getElementById('go-pro-btn');
    const adTopBanner = document.getElementById('ad-top-banner');
    const adInContent = document.getElementById('ad-in-content');
    const adStickyFooter = document.getElementById('ad-sticky-footer');
    const adStickyClose = document.getElementById('ad-sticky-close');
    const adStickySlot = document.getElementById('ad-sticky-slot');
    const adModal = document.getElementById('ad-modal');
    const adModalSlot = document.getElementById('ad-modal-slot');
    const adModalClose = document.getElementById('ad-modal-close');
    const licenseModal = document.getElementById('license-modal');
    const licenseInput = document.getElementById('license-input');
    const licenseVerifyBtn = document.getElementById('license-verify-btn');
    const licenseRemoveBtn = document.getElementById('license-remove-btn');
    const licenseCloseBtn = document.getElementById('license-close-btn');
    const licenseStatus = document.getElementById('license-status');

    // Initialize the game
    function initGame(useSaved = false) {
        // Reset game state
        towers = [[], [], []];
        selectedTower = null;
        moveCount = 0;
        isAnimating = false;
        hasStarted = false;
        clearTimer();
        updateTimer(0);
        undoStack = [];
        redoStack = [];
        updateMoveCounter();
        
        // Clear all disks
        document.querySelectorAll('.disk').forEach(disk => disk.remove());
        
        // Set number of disks from select or default
        if (useSaved) {
            const savedDisks = getSavedDisks();
            if (savedDisks) {
                diskCountSelect.value = String(savedDisks);
            }
        }
        diskCount = parseInt(diskCountSelect.value) || 3;
        
        // Create disks
        for (let i = diskCount; i > 0; i--) {
            const disk = document.createElement('div');
            disk.className = 'disk';
            disk.dataset.size = i;
            disk.textContent = i;
            
            // Add to first tower
            towers[0].unshift(disk);
            towersEl[0].insertBefore(disk, towersEl[0].querySelector('.base'));
        }
        
        // Reset selected state
        towersEl.forEach(tower => tower.classList.remove('selected'));
        
        // Close win modal if open
        winModal.style.display = 'none';

        // Update UI state
        updateDifficultyLabel();
        updateBestDisplay();
        updateControlsState();
        updateRatingStars(0);
        clearHistoryUI();
        announce(`New game started with ${diskCount} disks.`);

        // Restore mute state
        isMuted = getSavedMute();
        if (muteBtn) {
            muteBtn.setAttribute('aria-pressed', isMuted ? 'true' : 'false');
            const icon = muteBtn.querySelector('i');
            if (icon) icon.className = isMuted ? 'fas fa-volume-mute' : 'fas fa-volume-up';
        }
        // Offer continue if saved game exists
        updateContinueVisibility();

        // Load ads (non-blocking)
        if (!isPro()) injectAds();

        // Ensure pause overlay is hidden on start
        if (pauseOverlay) pauseOverlay.setAttribute('hidden', '');
    }

    // Update move counter display
    function updateMoveCounter() {
        moveCounterEl.textContent = moveCount;
    }

    // Timer helpers
    function startTimerIfNeeded() {
        if (hasStarted) return;
        hasStarted = true;
        startTimeMs = Date.now();
        timerInterval = setInterval(() => {
            updateTimer(Date.now() - startTimeMs + elapsedMs);
        }, 250);
    }

    function clearTimer() {
        if (timerInterval) clearInterval(timerInterval);
        timerInterval = null;
        startTimeMs = 0;
        elapsedMs = 0;
    }

    function stopTimer() {
        if (!timerInterval) return;
        clearInterval(timerInterval);
        timerInterval = null;
        elapsedMs = Date.now() - startTimeMs + elapsedMs;
    }

    function updateTimer(ms) {
        const totalSeconds = Math.floor(ms / 1000);
        const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
        const seconds = String(totalSeconds % 60).padStart(2, '0');
        if (timerEl) timerEl.textContent = `${minutes}:${seconds}`;
    }

    // Difficulty label based on disk count
    function updateDifficultyLabel() {
        if (!difficultyLabelEl) return;
        const n = diskCount;
        const label = n <= 3 ? 'Beginner' : n <= 5 ? 'Intermediate' : n === 6 ? 'Advanced' : 'Expert';
        difficultyLabelEl.textContent = label;
    }

    // Best score persistence
    function storageKey(n) {
        return `hanoi_best_${n}`;
    }

    function getBest(n) {
        try {
            const raw = localStorage.getItem(storageKey(n));
            return raw ? JSON.parse(raw) : null;
        } catch (e) { return null; }
    }

    function setBest(n, best) {
        try {
            localStorage.setItem(storageKey(n), JSON.stringify(best));
        } catch (e) {}
    }

    function updateBestDisplay() {
        const best = getBest(diskCount);
        if (!best) {
            if (bestMovesEl) bestMovesEl.textContent = '-';
            if (bestTimeEl) bestTimeEl.textContent = '--:--';
            return;
        }
        if (bestMovesEl) bestMovesEl.textContent = String(best.moves);
        if (bestTimeEl) bestTimeEl.textContent = formatMs(best.timeMs);
    }

    function formatMs(ms) {
        const totalSeconds = Math.floor(ms / 1000);
        const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
        const seconds = String(totalSeconds % 60).padStart(2, '0');
        return `${minutes}:${seconds}`;
    }

    // Rating stars
    function updateRatingStars(stars) {
        if (!ratingStarsEl) return;
        const starIcons = Array.from(ratingStarsEl.querySelectorAll('i'));
        starIcons.forEach((icon, index) => {
            icon.className = index < stars ? 'fas fa-star' : 'far fa-star';
        });
    }

    function computeStars(moves) {
        const minMoves = Math.pow(2, diskCount) - 1;
        if (moves <= minMoves) return 5;
        const ratio = moves / minMoves;
        if (ratio <= 1.2) return 4;
        if (ratio <= 1.5) return 3;
        if (ratio <= 2.0) return 2;
        return 1;
    }

    // Check if a move is valid
    function isValidMove(fromTower, toTower) {
        if (fromTower.length === 0) return false;
        
        const diskToMove = fromTower[fromTower.length - 1];
        const topDiskOnTarget = toTower[toTower.length - 1];
        
        return !topDiskOnTarget || 
               parseInt(diskToMove.dataset.size) < parseInt(topDiskOnTarget.dataset.size);
    }

    // Move disk animation
    async function moveDisk(fromIndex, toIndex) {
        if (isAnimating) return false;
        if (isSolving) {
            // Allow solver to move, but block manual double-moves
        }
        isAnimating = true;
        
        const fromTower = towers[fromIndex];
        const toTower = towers[toIndex];
        const disk = fromTower[fromTower.length - 1];
        
        if (!disk) {
            isAnimating = false;
            return false;
        }
        
        // Add moving class for animation
        disk.classList.add('moving');
        
        // Calculate positions
        const fromTowerEl = towersEl[fromIndex];
        const toTowerEl = towersEl[toIndex];
        const diskHeight = disk.offsetHeight;
        const liftHeight = 50; // How high to lift the disk
        
        // Get positions
        const fromRect = fromTowerEl.getBoundingClientRect();
        const toRect = toTowerEl.getBoundingClientRect();
        
        // Position disk absolutely for animation
        disk.style.position = 'fixed';
        disk.style.left = `${fromRect.left + fromRect.width / 2 - disk.offsetWidth / 2}px`;
        disk.style.top = `${fromRect.top + fromRect.height - (fromTower.length * diskHeight)}px`;
        disk.style.zIndex = '100';
        
        // Animate lifting up
        disk.style.transition = 'transform 0.3s ease';
        disk.style.transform = `translateY(-${liftHeight}px)`;
        
        // Wait for lift animation to complete
        await new Promise(resolve => setTimeout(resolve, 300));
        
        // Animate moving left/right
        const xOffset = (toRect.left + toRect.width / 2) - (fromRect.left + fromRect.width / 2);
        disk.style.transform = `translate(${xOffset}px, -${liftHeight}px)`;
        
        // Wait for move animation to complete
        await new Promise(resolve => setTimeout(resolve, 300));
        
        // Calculate drop position
        const dropY = toRect.top + toRect.height - (toTower.length * diskHeight) - liftHeight;
        disk.style.transform = `translate(${xOffset}px, ${dropY - (fromRect.top + fromRect.height - (fromTower.length * diskHeight))}px)`;
        
        // Wait for drop animation to complete
        await new Promise(resolve => setTimeout(resolve, 300));
        
        // Reset styles and add to new tower
        disk.style.position = '';
        disk.style.left = '';
        disk.style.top = '';
        disk.style.transform = '';
        disk.style.transition = '';
        disk.style.zIndex = '';
        disk.classList.remove('moving');
        
        // Update game state
        const movedDisk = fromTower.pop();
        toTower.push(movedDisk);
        toTowerEl.insertBefore(movedDisk, toTowerEl.querySelector('.base'));
        
        // Update move counter
        moveCount++;
        updateMoveCounter();
        updateControlsState();
        
        // Check for win
        if (checkWin()) {
            showWinMessage();
        }
        
        isAnimating = false;
        return true;
    }

    // Check if the player has won
    function checkWin() {
        return towers[1].length === diskCount || towers[2].length === diskCount;
    }

    // Show win message
    function showWinMessage() {
        stopTimer();
        const timeMs = elapsedMs || (hasStarted ? Date.now() - startTimeMs : 0);
        const minMoves = Math.pow(2, diskCount) - 1;
        if (finalMovesEl) finalMovesEl.textContent = moveCount;
        if (finalTimeEl) finalTimeEl.textContent = formatMs(timeMs);
        if (minimalMovesEl) minimalMovesEl.textContent = String(minMoves);
        const stars = computeStars(moveCount);
        updateRatingStars(stars);
        saveBestIfImproved(moveCount, timeMs);
        updateBestDisplay();
        playSound('win');
        announce(`Puzzle solved in ${moveCount} moves and ${formatMs(timeMs)}.`);
        winModal.style.display = 'flex';
    }

    function saveBestIfImproved(moves, timeMs) {
        const best = getBest(diskCount);
        if (!best || moves < best.moves || (moves === best.moves && timeMs < best.timeMs)) {
            setBest(diskCount, { moves, timeMs });
        }
    }

    function updateControlsState() {
        if (undoBtn) undoBtn.disabled = undoStack.length === 0;
        if (redoBtn) redoBtn.disabled = redoStack.length === 0;
        if (solveBtn) {
            const canSolveFromStart = towers[0].length === diskCount && towers[1].length === 0 && towers[2].length === 0 && moveCount === 0;
            solveBtn.disabled = !canSolveFromStart || isSolving;
        }
        if (pauseBtn) pauseBtn.disabled = isSolving;
        if (hintBtn) hintBtn.disabled = isSolving;
    }

    function clearHistoryUI() {
        if (!moveHistoryEl) return;
        moveHistoryEl.innerHTML = '';
    }

    function addHistoryEntry(from, to, size) {
        if (!moveHistoryEl) return;
        const item = document.createElement('li');
        item.textContent = `Move disk ${size} from ${from + 1} to ${to + 1}`;
        moveHistoryEl.appendChild(item);
        // Scroll to bottom
        moveHistoryEl.parentElement.scrollTop = moveHistoryEl.parentElement.scrollHeight;
    }

    // Sounds
    let audioCtx = null;
    function ensureAudio() {
        if (!audioCtx) {
            try { audioCtx = new (window.AudioContext || window.webkitAudioContext)(); } catch (e) { audioCtx = null; }
        }
        return audioCtx;
    }

    function playSound(name) {
        if (isMuted) return;
        const ctx = ensureAudio();
        if (!ctx) return;
        const now = ctx.currentTime;
        if (name === 'move') {
            const o = ctx.createOscillator();
            const g = ctx.createGain();
            o.type = 'sine';
            o.frequency.setValueAtTime(550, now);
            g.gain.setValueAtTime(0.001, now);
            g.gain.exponentialRampToValueAtTime(0.1, now + 0.01);
            g.gain.exponentialRampToValueAtTime(0.0001, now + 0.15);
            o.connect(g).connect(ctx.destination);
            o.start(now);
            o.stop(now + 0.16);
        } else if (name === 'invalid') {
            const o = ctx.createOscillator();
            const g = ctx.createGain();
            o.type = 'square';
            o.frequency.setValueAtTime(220, now);
            g.gain.setValueAtTime(0.08, now);
            g.gain.exponentialRampToValueAtTime(0.0001, now + 0.2);
            o.connect(g).connect(ctx.destination);
            o.start(now);
            o.stop(now + 0.21);
        } else if (name === 'win') {
            const freqs = [523.25, 659.25, 783.99];
            freqs.forEach((f, i) => {
                const o = ctx.createOscillator();
                const g = ctx.createGain();
                o.type = 'triangle';
                o.frequency.setValueAtTime(f, now + i * 0.08);
                g.gain.setValueAtTime(0.001, now + i * 0.08);
                g.gain.exponentialRampToValueAtTime(0.12, now + i * 0.09);
                g.gain.exponentialRampToValueAtTime(0.0001, now + i * 0.28);
                o.connect(g).connect(ctx.destination);
                o.start(now + i * 0.08);
                o.stop(now + i * 0.3);
            });
        }
    }

    // Handle tower click
    function handleTowerClick(towerIndex) {
        if (isAnimating) return;
        if (isPaused()) return;
        if (isSolving) return;
        
        // If no tower is selected, select this one
        if (selectedTower === null) {
            // Can't select an empty tower
            if (towers[towerIndex].length === 0) return;
            
            selectedTower = towerIndex;
            towersEl[towerIndex].classList.add('selected');
            return;
        }
        
        // If same tower is clicked twice, deselect it
        if (selectedTower === towerIndex) {
            towersEl[towerIndex].classList.remove('selected');
            selectedTower = null;
            return;
        }
        
        // Try to move disk from selected tower to this one
        if (isValidMove(towers[selectedTower], towers[towerIndex])) {
            const from = selectedTower;
            const to = towerIndex;
            const disk = towers[from][towers[from].length - 1];
            const size = disk ? parseInt(disk.dataset.size) : null;
            startTimerIfNeeded();
            moveDisk(from, to).then(success => {
                if (success) {
                    undoStack.push({ from, to, size });
                    redoStack = [];
                    addHistoryEntry(from, to, size);
                    updateControlsState();
                    playSound('move');
                }
            });
        } else {
            // Invalid move - show feedback
            const disk = towers[selectedTower][towers[selectedTower].length - 1];
            if (disk) {
                disk.classList.add('invalid-move');
                setTimeout(() => disk.classList.remove('invalid-move'), 500);
            }
            playSound('invalid');
            announce('Invalid move.');
        }
        
        // Deselect the tower
        towersEl[selectedTower].classList.remove('selected');
        selectedTower = null;
    }

    // Keyboard support
    function handleTowerKey(e, index) {
        if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') {
            e.preventDefault();
            handleTowerClick(index);
        } else if (e.key === 'ArrowLeft') {
            e.preventDefault();
            const prev = (index + 2) % 3;
            towersEl[prev].focus();
        } else if (e.key === 'ArrowRight') {
            e.preventDefault();
            const next = (index + 1) % 3;
            towersEl[next].focus();
        } else if (e.key === '1' || e.key === '2' || e.key === '3') {
            e.preventDefault();
            handleTowerClick(parseInt(e.key, 10) - 1);
        }
    }

    // Drag and Drop
    function enableDragAndDrop() {
        towersEl.forEach((towerEl, towerIndex) => {
            towerEl.addEventListener('dragover', (e) => {
                if (isPaused() || isSolving) return;
                e.preventDefault();
            });
            towerEl.addEventListener('drop', (e) => {
                if (isPaused() || isSolving) return;
                const from = parseInt(e.dataTransfer.getData('fromTower'), 10);
                if (Number.isNaN(from)) return;
                if (!isValidMove(towers[from], towers[towerIndex])) { playSound('invalid'); return; }
                const disk = towers[from][towers[from].length - 1];
                const size = disk ? parseInt(disk.dataset.size) : null;
                startTimerIfNeeded();
                moveDisk(from, towerIndex).then((ok) => {
                    if (ok) {
                        undoStack.push({ from, to: towerIndex, size });
                        redoStack = [];
                        addHistoryEntry(from, towerIndex, size);
                        updateControlsState();
                        playSound('move');
                    }
                });
            });
        });
    }

    function makeTopDisksDraggable() {
        // Reset all
        document.querySelectorAll('.disk').forEach((d) => {
            d.setAttribute('draggable', 'false');
            d.removeEventListener('dragstart', noop);
        });
        // Make only top disks draggable
        towers.forEach((towerArr, index) => {
            const disk = towerArr[towerArr.length - 1];
            if (!disk) return;
            disk.setAttribute('draggable', 'true');
            disk.addEventListener('dragstart', (e) => {
                if (isPaused() || isSolving) { e.preventDefault(); return; }
                e.dataTransfer.setData('fromTower', String(index));
                e.dataTransfer.effectAllowed = 'move';
            });
        });
    }

    function noop() {}

    // Undo/Redo
    function applyMoveInstant(from, to) {
        const fromTower = towers[from];
        const toTower = towers[to];
        const disk = fromTower[fromTower.length - 1];
        if (!disk) return false;
        fromTower.pop();
        toTower.push(disk);
        const toTowerEl = towersEl[to];
        toTowerEl.insertBefore(disk, toTowerEl.querySelector('.base'));
        return true;
    }

    function undo() {
        if (undoStack.length === 0 || isAnimating || isSolving) return;
        if (isPaused()) return;
        const last = undoStack.pop();
        const { from, to, size } = last;
        const success = applyMoveInstant(to, from);
        if (success) {
            moveCount = Math.max(0, moveCount - 1);
            updateMoveCounter();
            redoStack.push(last);
            // Remove last history entry UI
            if (moveHistoryEl && moveHistoryEl.lastElementChild) {
                moveHistoryEl.removeChild(moveHistoryEl.lastElementChild);
            }
            updateControlsState();
        }
    }

    function redo() {
        if (redoStack.length === 0 || isAnimating || isSolving) return;
        if (isPaused()) return;
        const next = redoStack.pop();
        const { from, to, size } = next;
        const success = applyMoveInstant(from, to);
        if (success) {
            moveCount += 1;
            updateMoveCounter();
            undoStack.push(next);
            addHistoryEntry(from, to, size);
            updateControlsState();
            if (checkWin()) {
                showWinMessage();
            }
        }
    }

    // Pause/Resume
    function isPaused() {
        return pauseOverlay && !pauseOverlay.hasAttribute('hidden');
    }

    function pauseGame() {
        if (isPaused() || isSolving) return;
        stopTimer();
        if (pauseOverlay) pauseOverlay.removeAttribute('hidden');
        if (pauseBtn) {
            pauseBtn.setAttribute('aria-pressed', 'true');
            pauseBtn.innerHTML = '<i class="fas fa-play"></i> Resume';
        }
    }

    function resumeGame() {
        if (!isPaused()) return;
        if (pauseOverlay) pauseOverlay.setAttribute('hidden', '');
        if (pauseBtn) {
            pauseBtn.setAttribute('aria-pressed', 'false');
            pauseBtn.innerHTML = '<i class="fas fa-pause"></i> Pause';
        }
        // Restart timer
        hasStarted = false; // to allow startTimerIfNeeded
        startTimerIfNeeded();
    }

    // Hint (compute next optimal move using recursion)
    function computeOptimalMoves(n, from, aux, to) {
        const result = [];
        (function gen(m, a, b, c) {
            if (m === 0) return;
            gen(m - 1, a, c, b);
            result.push([a, c]);
            gen(m - 1, b, a, c);
        })(n, from, aux, to);
        return result;
    }

    function getCurrentStateMoves() {
        // Try to align with optimal sequence from start; find first move not yet made
        const seq = computeOptimalMoves(diskCount, 0, 1, 2);
        const k = undoStack.length; // number of moves performed
        return seq.slice(k);
    }

    function hint() {
        if (isPaused() || isSolving) return;
        const remaining = getCurrentStateMoves();
        if (remaining.length === 0) return;
        const [from, to] = remaining[0];
        // Highlight suggested towers briefly
        [from, to].forEach((idx) => {
            const el = towersEl[idx];
            if (!el) return;
            el.classList.add('selected');
            setTimeout(() => el.classList.remove('selected'), 600);
        });
        announce(`Hint: move from tower ${from + 1} to ${to + 1}.`);
    }

    // Save/Restore in-progress game
    function serializeGame() {
        const data = towers.map((t) => t.map((d) => parseInt(d.dataset.size, 10)));
        return {
            towers: data,
            moveCount,
            diskCount,
            elapsedMs: hasStarted ? (Date.now() - startTimeMs + elapsedMs) : elapsedMs,
        };
    }

    function saveGame() {
        try {
            localStorage.setItem('hanoi_save', JSON.stringify(serializeGame()));
        } catch (e) {}
        updateContinueVisibility();
    }

    function updateContinueVisibility() {
        if (!continueBtn) return;
        try {
            const raw = localStorage.getItem('hanoi_save');
            continueBtn.hidden = !raw;
        } catch (e) { continueBtn.hidden = true; }
    }

    function restoreGame() {
        let data = null;
        try { data = JSON.parse(localStorage.getItem('hanoi_save') || 'null'); } catch (e) { data = null; }
        if (!data) return;
        // Initialize fresh with correct disk count
        diskCountSelect.value = String(data.diskCount || 3);
        towers = [[], [], []];
        document.querySelectorAll('.disk').forEach((d) => d.remove());
        [0,1,2].forEach((i) => {
            (data.towers[i] || []).forEach((size) => {
                const disk = document.createElement('div');
                disk.className = 'disk';
                disk.dataset.size = size;
                disk.textContent = size;
                towers[i].push(disk);
                towersEl[i].insertBefore(disk, towersEl[i].querySelector('.base'));
            });
        });
        moveCount = data.moveCount || 0;
        updateMoveCounter();
        elapsedMs = data.elapsedMs || 0;
        updateTimer(elapsedMs);
        hasStarted = false;
        undoStack = []; redoStack = []; clearHistoryUI();
        updateControlsState();
        announce('Game restored.');
    }

    // Ads
    const AD_URLS = [
        'https://www.profitableratecpm.com/mvyig4fw2?key=9d082a398f2b1455f5965c0aec8b6a0c',
        'https://www.profitableratecpm.com/i1m19wngfn?key=34eaaa4cfabf2f61d18593b4c8c8b35a',
        'https://www.profitableratecpm.com/ywsjhr2t9?key=3b24f96f1ee79daa7b229b5affab7006',
        'https://www.profitableratecpm.com/eia1pa1dty?key=160ee4de7d9f92aba968ecb88b3c1435',
        'https://www.profitableratecpm.com/z6mt31ac?key=b297a74bf4bf65b55c376be88f112f9c',
        'https://www.profitableratecpm.com/e54dji0b9g?key=d3fb0b347ba82804df12b3ec7fa01041',
        'https://www.profitableratecpm.com/imm1a4ayyq?key=bdb4e643ceacc9b3a1dc3fbf9c9f0b79'
    ];

    function injectScript(url, parent) {
        const s = document.createElement('script');
        s.async = true;
        s.src = url;
        s.onerror = () => {
            // Fallback to iframe if script fails
            injectIframe(url, parent);
        };
        ;(parent || document.body).appendChild(s);
    }

    function injectIframe(url, parent) {
        const iframe = document.createElement('iframe');
        iframe.src = url;
        iframe.style.width = '100%';
        iframe.style.height = '100%';
        iframe.style.border = '0';
        iframe.loading = 'lazy';
        ;(parent || document.body).appendChild(iframe);
    }

    function injectAds() {
        // Basic safety: don't inject multiple times
        if (document.body.getAttribute('data-ads-initialized') === '1') return;
        document.body.setAttribute('data-ads-initialized', '1');
        // Placeholders for containers with robustness checks
        fillAdSlot(adTopBanner, AD_URLS[0]);
        fillAdSlot(adInContent, AD_URLS[1]);
        fillAdSlot(adStickySlot, AD_URLS[2]);
        fillAdSlot(adModalSlot, AD_URLS[3]);
        // Avoid global injections to reduce layout shifts
        // injectScript(AD_URLS[4]);
        // injectScript(AD_URLS[5]);
        // injectScript(AD_URLS[6]);
        // Show sticky after small delay
        setTimeout(() => { if (adStickyFooter) adStickyFooter.hidden = false; }, 1500);
    }

    function fillAdSlot(container, url) {
        if (!container) return;
        injectScript(url, container);
        // Fallback check: if slot remains empty, try iframe after 2s
        setTimeout(() => {
            if (container.children.length === 0) {
                injectIframe(url, container);
            }
        }, 2000);
    }

    // Licensing (client-side verification + server validation hook)
    const LICENSE_KEY_STORAGE = 'hanoi_license_key_v1';
    const LICENSE_STATUS_STORAGE = 'hanoi_license_status_v1';

    function isPro() {
        try { return localStorage.getItem(LICENSE_STATUS_STORAGE) === 'valid'; } catch (e) { return false; }
    }

    function setPro(valid, key) {
        try {
            localStorage.setItem(LICENSE_STATUS_STORAGE, valid ? 'valid' : 'invalid');
            if (valid && key) localStorage.setItem(LICENSE_KEY_STORAGE, key);
        } catch (e) {}
        updateProUI();
    }

    function updateProUI() {
        const pro = isPro();
        // Hide ads when Pro
        if (pro) {
            [adTopBanner, adInContent, adStickyFooter, adModal].forEach((el) => { if (el) el.style.display = 'none'; });
        }
        if (goProBtn) goProBtn.style.display = pro ? 'none' : '';
        if (licenseRemoveBtn) licenseRemoveBtn.style.display = pro ? '' : 'none';
    }

    async function fetchProStatus() { /* disabled: must verify key from user */ }
    async function verifyLicense(key) {
        if (!key || key.length < 10) {
            return { ok: false, message: 'Invalid key format.' };
        }
        const gameConfig = {
            id: 'tower-of-hanoi',
            title: 'Tower of Hanoi: Ultimate 3D Logic Puzzle',
            description: 'Master the classic mathematical challenge. Move all disks from rod 1 to rod 3 while never placing a larger disk on a smaller one.',
            image: './tower-of-hanoi.png',
        };
        const gameId = (window.gameConfig && window.gameConfig.id) || gameConfig.id;
        // Prefer same-origin proxy to avoid CORS, fallback to remote URL if needed
        const proxyUrl = 'license-proxy.php';
        const remoteUrl = (window.LICENSE_VERIFY_URL) || 'https://rskworld.in/games/html5-games/verify-license.php';

        const tryJson = (url) => fetch(url, {
            method: 'POST',
            mode: 'cors',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ key, gameId })
        });
        const tryForm = (url) => fetch(url, {
            method: 'POST',
            mode: 'cors',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({ key, gameId }).toString()
        });

        const parse = async (res) => {
            if (!res || !res.ok) return { ok: false, message: `Server unavailable (${res ? res.status : 'no response'})` };
            try {
                const data = await res.json();
                return { ok: !!data.valid, message: data.message || (data.valid ? 'License verified.' : 'Invalid license.') };
            } catch (_) {
                return { ok: false, message: 'Invalid server response.' };
            }
        };

        try {
            // Try proxy first
            let res = await tryJson(proxyUrl);
            let out = await parse(res);
            if (out.ok || (out.message && out.message.toLowerCase().includes('invalid license'))) return out;
            // Try proxy form
            res = await tryForm(proxyUrl);
            out = await parse(res);
            if (out.ok || (out.message && out.message.toLowerCase().includes('invalid license'))) return out;
            // Try remote JSON
            res = await tryJson(remoteUrl);
            out = await parse(res);
            if (out.ok || (out.message && out.message.toLowerCase().includes('invalid license'))) return out;
            res = await tryForm(remoteUrl);
            out = await parse(res);
            if (out.ok || (out.message && out.message.toLowerCase().includes('invalid license'))) return out;
        } catch (_) { /* ignore and fallback */ }

        return { ok: false, message: 'Could not reach verification server. Try again later.' };
    }

    function openAdModal() {
        if (!adModal) return;
        adModal.style.display = 'flex';
    }
    function closeAdModal() {
        if (!adModal) return;
        adModal.style.display = 'none';
    }

    // History toggle
    function toggleHistoryPanel() {
        if (!historyPanelEl) return;
        const isHidden = historyPanelEl.hasAttribute('hidden');
        if (isHidden) {
            historyPanelEl.removeAttribute('hidden');
            if (historyToggleBtn) historyToggleBtn.setAttribute('aria-expanded', 'true');
        } else {
            historyPanelEl.setAttribute('hidden', '');
            if (historyToggleBtn) historyToggleBtn.setAttribute('aria-expanded', 'false');
        }
    }

    // Solver
    function generateMoves(n, from, aux, to, list) {
        if (n === 0) return;
        generateMoves(n - 1, from, to, aux, list);
        list.push([from, to]);
        generateMoves(n - 1, aux, from, to, list);
    }

    async function autoSolve() {
        if (isSolving) return;
        const canSolveFromStart = towers[0].length === diskCount && towers[1].length === 0 && towers[2].length === 0 && moveCount === 0;
        if (!canSolveFromStart) {
            announce('Auto-solve is only available at the start of a new game.');
            return;
        }
        isSolving = true;
        updateControlsState();
        startTimerIfNeeded();
        const moves = [];
        generateMoves(diskCount, 0, 1, 2, moves);
        for (const [from, to] of moves) {
            const disk = towers[from][towers[from].length - 1];
            const size = disk ? parseInt(disk.dataset.size) : null;
            // eslint-disable-next-line no-await-in-loop
            const ok = await moveDisk(from, to);
            if (!ok) break;
            undoStack.push({ from, to, size });
            redoStack = [];
            addHistoryEntry(from, to, size);
            updateControlsState();
            playSound('move');
            if (!isSolving) break;
        }
        isSolving = false;
        updateControlsState();
    }

    // Announce helper
    function announce(text) {
        if (!ariaLiveEl) return;
        ariaLiveEl.textContent = '';
        setTimeout(() => { ariaLiveEl.textContent = text; }, 0);
    }

    // Event Listeners
    towersEl.forEach((tower, index) => {
        tower.addEventListener('click', () => handleTowerClick(index));
        tower.addEventListener('keydown', (e) => handleTowerKey(e, index));
    });
    
    newGameBtn.addEventListener('click', () => initGame(false));
    if (playAgainBtn) {
        playAgainBtn.addEventListener('click', () => {
            winModal.style.display = 'none';
            initGame(false);
        });
    }
    if (nextLevelBtn) {
        nextLevelBtn.addEventListener('click', () => {
            winModal.style.display = 'none';
            const next = Math.min(7, diskCount + 1);
            diskCountSelect.value = String(next);
            initGame(false);
        });
    }
    
    diskCountSelect.addEventListener('change', () => {
        saveDisks(parseInt(diskCountSelect.value, 10));
        initGame(false);
    });
    if (undoBtn) undoBtn.addEventListener('click', undo);
    if (redoBtn) redoBtn.addEventListener('click', redo);
    if (historyToggleBtn) historyToggleBtn.addEventListener('click', toggleHistoryPanel);
    if (muteBtn) {
        muteBtn.addEventListener('click', () => {
            isMuted = !isMuted;
            muteBtn.setAttribute('aria-pressed', isMuted ? 'true' : 'false');
            const icon = muteBtn.querySelector('i');
            if (icon) icon.className = isMuted ? 'fas fa-volume-mute' : 'fas fa-volume-up';
            saveMute(isMuted);
        });
    }
    if (solveBtn) solveBtn.addEventListener('click', autoSolve);

    document.addEventListener('keydown', (e) => {
        const target = e.target;
        const isTypingElement = target && (target.tagName === 'INPUT' || target.tagName === 'SELECT' || target.tagName === 'TEXTAREA');
        if (isTypingElement) return;
        if (e.key.toLowerCase() === 'u') undo();
        if (e.key.toLowerCase() === 'r') redo();
        if (e.key.toLowerCase() === 'n') initGame(false);
        if (e.key.toLowerCase() === 'h') toggleHistoryPanel();
        if (e.key.toLowerCase() === 'm' && muteBtn) muteBtn.click();
        if (e.key.toLowerCase() === 'p' && pauseBtn) pauseBtn.click();
    });

    if (pauseBtn) pauseBtn.addEventListener('click', () => {
        if (isPaused()) { resumeGame(); } else { pauseGame(); }
    });
    if (hintBtn) hintBtn.addEventListener('click', hint);
    if (continueBtn) continueBtn.addEventListener('click', restoreGame);
    if (adStickyClose) adStickyClose.addEventListener('click', () => { if (adStickyFooter) adStickyFooter.hidden = true; });
    if (adModalClose) adModalClose.addEventListener('click', closeAdModal);

    if (goProBtn) goProBtn.addEventListener('click', () => {
        if (!licenseModal) return;
        licenseStatus.textContent = '';
        licenseModal.style.display = 'flex';
        if (licenseInput) licenseInput.focus();
    });
    if (licenseCloseBtn) licenseCloseBtn.addEventListener('click', () => { if (licenseModal) licenseModal.style.display = 'none'; });
    if (licenseVerifyBtn) licenseVerifyBtn.addEventListener('click', async () => {
        const key = licenseInput ? licenseInput.value.trim() : '';
        licenseStatus.textContent = 'Verifying...';
        const result = await verifyLicense(key);
        licenseStatus.textContent = result.message;
        if (result.ok) { setPro(true, key); if (licenseModal) licenseModal.style.display = 'none'; }
    });
    if (licenseRemoveBtn) licenseRemoveBtn.addEventListener('click', () => { setPro(false); announce('License removed.'); });

    // On load, Pro is disabled until user verifies via server
    updateProUI();

    // Periodic autosave
    setInterval(saveGame, 5000);

    // Enable DnD
    enableDragAndDrop();
    // Re-evaluate draggable top disks whenever a disk moves by observing DOM changes
    const observer = new MutationObserver(() => makeTopDisksDraggable());
    towersEl.forEach((el) => observer.observe(el, { childList: true }));
    makeTopDisksDraggable();

    // Initialize the game
    initGame(true);

    // Persist settings helpers
    function saveDisks(n) {
        try { localStorage.setItem('hanoi_disks', String(n)); } catch (e) {}
    }
    function getSavedDisks() {
        try { const v = localStorage.getItem('hanoi_disks'); return v ? parseInt(v, 10) : null; } catch (e) { return null; }
    }
    function saveMute(m) {
        try { localStorage.setItem('hanoi_mute', m ? '1' : '0'); } catch (e) {}
    }
    function getSavedMute() {
        try { return localStorage.getItem('hanoi_mute') === '1'; } catch (e) { return false; }
    }

    // Disk count saved in the change handler above
});
