import { browser } from "$app/environment";
import type { Team, Round, FinalRound, GameSettings, GamePhase, QuestionResult } from "$lib/types/kuldvillak";

// Game session state that syncs across tabs
export interface GameSessionState {
    // Game data
    name: string;
    settings: GameSettings;
    teams: Team[];
    rounds: Round[];
    finalRound: FinalRound | null;

    // Current game state
    phase: GamePhase;
    currentRoundIndex: number;
    activeTeamId: string | null;

    // Intro animation state
    introCategoryIndex: number; // Which category is being shown during intro
    categoriesIntroduced: boolean; // Have all categories been introduced for this round
    boardRevealed: boolean; // Has the board been revealed (prices faded in) for this round

    // Question state
    currentQuestion: {
        roundIndex: number;
        categoryIndex: number;
        questionIndex: number;
    } | null;
    showAnswer: boolean;
    wrongTeamIds: string[]; // Teams that answered wrong for current question
    lastAnsweredTeamId: string | null; // Track who answered last
    lastAnswerCorrect: boolean | null; // Was it correct or wrong

    // Daily Double
    dailyDoubleWager: number | null;

    // Final Round
    finalCategoryRevealed: boolean; // Has the final category been revealed
    finalWagers: Record<string, number>;
    finalAnswers: Record<string, string>;
    finalRevealed: string[]; // Team IDs that have been revealed

    // Timer
    timerRunning: boolean;
    timerSeconds: number;
    timerMax: number;

    // Timeout countdowns (for displaying messages)
    timeoutCountdown: number | null; // "Revealing answer in X seconds"
    revealCountdown: number | null;  // "Returning to board in X seconds"
    skippingQuestion: boolean; // True when moderator clicked skip

    // Question tracking
    questionsAnswered: number; // How many questions have been answered
    currentQuestionNumber: number; // Which question number is this (1-30)
    questionResults: QuestionResult[]; // Results of answered questions
}

const CHANNEL_NAME = "kuldvillak-game-session";
const STORAGE_KEY = "kuldvillak-game-session";

class GameSessionStore {
    private channel: BroadcastChannel | null = null;
    private timerInterval: ReturnType<typeof setInterval> | null = null;
    private isTimerOwner = false; // Only one tab should own the timer
    private projectorWindow: Window | null = null; // Track projector window for cleanup
    state = $state<GameSessionState | null>(null);

    constructor() {
        if (browser) {
            // Setup broadcast channel for cross-tab sync
            this.channel = new BroadcastChannel(CHANNEL_NAME);
            this.channel.onmessage = (event) => {
                if (event.data.type === "STATE_UPDATE") {
                    this.state = event.data.state;
                } else if (event.data.type === "REQUEST_STATE") {
                    // Another tab is requesting the current state
                    if (this.state) {
                        this.broadcast("STATE_UPDATE", this.state);
                    }
                } else if (event.data.type === "TIMER_OWNER_CHECK") {
                    // Another tab is checking who owns the timer
                    if (this.isTimerOwner) {
                        this.channel?.postMessage({ type: "TIMER_OWNER_EXISTS" });
                    }
                } else if (event.data.type === "TIMER_OWNER_EXISTS") {
                    // Another tab owns the timer, don't start ours
                    this.isTimerOwner = false;
                }
            };

            // Try to load from localStorage
            const saved = localStorage.getItem(STORAGE_KEY);
            if (saved) {
                try {
                    this.state = JSON.parse(saved);
                    // Timer will be started by moderator view via enableTimerControl()
                } catch {
                    // Invalid data
                }
            }

            // Request state from other tabs
            this.channel.postMessage({ type: "REQUEST_STATE" });
        }
    }

    private broadcast(type: string, state: GameSessionState) {
        if (this.channel) {
            // Use $state.snapshot to get plain object from Proxy
            const plainState = $state.snapshot(state);
            this.channel.postMessage({ type, state: plainState });
        }
    }

    private persist() {
        if (browser && this.state) {
            // Use $state.snapshot to get plain object from Proxy
            const plainState = $state.snapshot(this.state);
            localStorage.setItem(STORAGE_KEY, JSON.stringify(plainState));
            this.broadcast("STATE_UPDATE", this.state);
        }
    }

    // Initialize a new game session
    startGame(data: {
        name: string;
        settings: GameSettings;
        teams: Team[];
        rounds: Round[];
        finalRound: FinalRound | null;
    }) {
        // Deep clone the data to remove any Proxy objects
        const plainData = JSON.parse(JSON.stringify(data));

        this.state = {
            ...plainData,
            phase: "intro" as const,
            currentRoundIndex: 0,
            activeTeamId: null,
            introCategoryIndex: -1,
            categoriesIntroduced: false,
            boardRevealed: false,
            currentQuestion: null,
            showAnswer: false,
            wrongTeamIds: [],
            lastAnsweredTeamId: null,
            lastAnswerCorrect: null,
            dailyDoubleWager: null,
            finalCategoryRevealed: false,
            finalWagers: {},
            finalAnswers: {},
            finalRevealed: [],
            timerRunning: false,
            timerSeconds: 0,
            timerMax: plainData.settings.defaultTimerSeconds ?? 10,
            timeoutCountdown: null,
            revealCountdown: null,
            skippingQuestion: false,
            questionsAnswered: 0,
            currentQuestionNumber: 0,
            questionResults: [],
        };
        // Timer will be started by moderator view via enableTimerControl()
        this.persist();
    }

    // ============================================
    // Intro Phase Management
    // ============================================

    startCategoryIntro() {
        if (!this.state) return;
        this.state.phase = "intro-categories";
        this.state.introCategoryIndex = 0;
        this.persist();
    }

    nextIntroCategory() {
        if (!this.state) return;
        const currentRound = this.state.rounds[this.state.currentRoundIndex];
        if (!currentRound) return;

        this.state.introCategoryIndex++;

        // Check if we've shown all categories - stay on villak screen
        if (this.state.introCategoryIndex >= currentRound.categories.length) {
            this.state.phase = "intro";
            this.state.introCategoryIndex = -1;
            this.state.categoriesIntroduced = true; // Mark categories as introduced
        }
        this.persist();
    }

    startBoard() {
        if (!this.state) return;
        this.state.phase = "board";
        this.persist();
    }

    markBoardRevealed() {
        if (!this.state) return;
        this.state.boardRevealed = true;
        this.persist();
    }

    // Transition to finished phase (show Kuldvillak screen)
    endGame() {
        if (!this.state) return;
        this.state.phase = "finished";
        this.persist();
    }

    // Fully clear the game session
    clearSession() {
        this.stopInternalTimer();
        this.state = null;
        if (browser) {
            localStorage.removeItem(STORAGE_KEY);
            this.broadcast("STATE_UPDATE", null as any);
            // Keep projectorWindow reference so we can detect if tab is still open
        }
    }

    // Open or focus the projector window
    openProjector() {
        if (browser) {
            // Reuse existing window if still open
            if (this.projectorWindow && !this.projectorWindow.closed) {
                return; // Already open, don't switch focus
            }
            this.projectorWindow = window.open("/kuldvillak/play?view=projector", "kuldvillak-projector");
        }
    }

    // ============================================
    // Question Management
    // ============================================

    selectQuestion(roundIndex: number, categoryIndex: number, questionIndex: number) {
        if (!this.state) return;

        const question = this.state.rounds[roundIndex]?.categories[categoryIndex]?.questions[questionIndex];
        if (!question || question.isRevealed) return;

        this.state.currentQuestion = { roundIndex, categoryIndex, questionIndex };
        this.state.wrongTeamIds = [];
        this.state.activeTeamId = null;
        this.state.lastAnswerCorrect = null;
        this.state.lastAnsweredTeamId = null;
        this.state.currentQuestionNumber = this.state.questionsAnswered + 1;

        if (question.isDailyDouble) {
            this.state.phase = "daily-double";
            this.state.dailyDoubleWager = null;
        } else {
            this.state.phase = "question";
        }

        this.state.showAnswer = false;

        // Reset timer
        this.state.timerSeconds = this.state.timerMax;
        this.state.timerRunning = false;

        this.persist();
    }

    setDailyDoubleWager(teamId: string, wager: number) {
        if (!this.state) return;
        this.state.activeTeamId = teamId;
        this.state.dailyDoubleWager = wager;
        this.state.phase = "question";
        this.persist();
    }

    toggleAnswer() {
        if (!this.state) return;
        this.state.showAnswer = !this.state.showAnswer;
        this.persist();
    }

    revealAnswer() {
        if (!this.state) return;
        this.state.showAnswer = true;
        this.persist();
    }

    // Mark answer correct - awards points, shows answer, closes after delay
    markCorrect(teamId: string) {
        if (!this.state || !this.state.currentQuestion) return;

        const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion;
        const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex];

        // Award points
        const team = this.state.teams.find(t => t.id === teamId);
        if (team) {
            const points = this.state.dailyDoubleWager ?? question.points;
            team.score += points;
        }

        // Track last answer
        this.state.lastAnsweredTeamId = teamId;
        this.state.lastAnswerCorrect = true;

        // Show answer and start reveal countdown
        this.state.showAnswer = true;
        this.state.revealCountdown = this.state.settings.answerRevealSeconds ?? 5;
        this.persist();
    }

    // Mark answer wrong - deducts points, adds to wrong list
    markWrong(teamId: string) {
        if (!this.state || !this.state.currentQuestion) return;

        const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion;
        const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex];

        // Deduct points if allowed
        const team = this.state.teams.find(t => t.id === teamId);
        if (team && this.state.settings.allowNegativeScores) {
            const points = this.state.dailyDoubleWager ?? question.points;
            team.score -= points;
        }

        // Track last answer
        this.state.lastAnsweredTeamId = teamId;
        this.state.lastAnswerCorrect = false;

        // Add to wrong list
        if (!this.state.wrongTeamIds.includes(teamId)) {
            this.state.wrongTeamIds.push(teamId);
        }

        // Clear active team for next selection
        this.state.activeTeamId = null;

        // Check if all teams have answered wrong
        const allTeamsWrong = this.state.teams.every(t => this.state!.wrongTeamIds.includes(t.id));
        if (allTeamsWrong) {
            // Everyone wrong - start reveal countdown
            this.state.timeoutCountdown = 5; // 5 seconds before showing answer
            this.persist();
        } else {
            this.persist();
        }
    }

    // Skip question - immediately reveal answer
    skipQuestion() {
        if (!this.state || !this.state.currentQuestion) return;

        // Stop timer if running
        this.state.timerRunning = false;
        this.state.activeTeamId = null;
        // Mark as skipping and immediately show answer
        this.state.skippingQuestion = true;
        this.state.showAnswer = true;
        this.state.revealCountdown = this.state.settings.answerRevealSeconds ?? 5;
        this.persist();
    }

    // Actually close the question and return to board
    private finalizeQuestion() {
        if (!this.state || !this.state.currentQuestion) return;

        const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion;
        const question = this.state.rounds[roundIndex].categories[categoryIndex].questions[questionIndex];

        // Save question result before resetting state
        const points = this.state.dailyDoubleWager ?? question.points;
        let pointsChange = 0;
        if (this.state.lastAnswerCorrect === true) {
            pointsChange = points;
        } else if (this.state.lastAnswerCorrect === false) {
            pointsChange = -points;
        }

        this.state.questionResults.push({
            categoryIndex,
            questionIndex,
            points: question.points,
            teamId: this.state.lastAnsweredTeamId,
            pointsChange,
            isDailyDouble: question.isDailyDouble,
            wager: this.state.dailyDoubleWager ?? undefined,
        });

        // Mark as revealed
        question.isRevealed = true;

        // Increment questions answered counter
        this.state.questionsAnswered++;

        // Reset state
        this.state.currentQuestion = null;
        this.state.showAnswer = false;
        this.state.wrongTeamIds = [];
        this.state.dailyDoubleWager = null;
        this.state.activeTeamId = null;
        this.state.timeoutCountdown = null;
        this.state.revealCountdown = null;
        this.state.skippingQuestion = false;
        this.state.phase = "board";

        // Check if round is complete
        this.checkRoundComplete();
        this.persist();
    }

    private checkRoundComplete() {
        if (!this.state) return;

        const currentRound = this.state.rounds[this.state.currentRoundIndex];
        const allRevealed = currentRound.categories.every(cat =>
            cat.questions.every(q => q.isRevealed)
        );

        if (allRevealed) {
            // Move to next round or final
            if (this.state.currentRoundIndex < this.state.rounds.length - 1) {
                this.state.currentRoundIndex++;
            } else if (this.state.settings.enableFinalRound && this.state.finalRound) {
                this.state.phase = "final-category";
            } else {
                this.state.phase = "finished";
            }
        }
    }

    // ============================================
    // Team & Score Management
    // ============================================

    setActiveTeam(teamId: string | null) {
        if (!this.state) return;
        this.state.activeTeamId = teamId;

        // Pause timer when a team is selected to answer
        if (teamId !== null && this.state.timerRunning) {
            this.state.timerRunning = false;
        }

        this.persist();
    }

    adjustScore(teamId: string, delta: number) {
        if (!this.state) return;
        const team = this.state.teams.find(t => t.id === teamId);
        if (team) {
            team.score += delta;
            this.persist();
        }
    }

    setScore(teamId: string, score: number) {
        if (!this.state) return;
        const team = this.state.teams.find(t => t.id === teamId);
        if (team) {
            team.score = score;
            this.persist();
        }
    }

    // ============================================
    // Round Management
    // ============================================

    nextRound() {
        if (!this.state) return;
        if (this.state.currentRoundIndex < this.state.rounds.length - 1) {
            this.state.currentRoundIndex++;
            this.state.phase = "intro";
            this.state.introCategoryIndex = -1;
            this.state.categoriesIntroduced = false; // Reset for new round
            this.state.boardRevealed = false; // Reset for new round
            this.state.questionResults = [];
            this.persist();
        }
    }

    goToFinalRound() {
        if (!this.state || !this.state.finalRound) return;
        this.state.phase = "final-intro";
        this.persist();
    }

    startFinalCategoryReveal() {
        if (!this.state) return;
        this.state.phase = "final-category";
        this.persist();
    }

    finishFinalCategoryReveal() {
        if (!this.state) return;
        // Go back to Kuldvillak screen, waiting for moderator to start question
        this.state.phase = "final-intro";
        this.state.finalCategoryRevealed = true;
        this.persist();
    }

    // ============================================
    // Final Round
    // ============================================

    showFinalQuestion() {
        if (!this.state) return;
        this.state.phase = "final-question";
        this.state.timerMax = 30; // Set 30 second timer for final round
        this.state.timerSeconds = 30;
        this.state.timerRunning = false;
        this.state.activeTeamId = null;
        this.state.finalRevealed = [];
        this.persist();
    }

    showFinalScores() {
        if (!this.state) return;
        this.state.phase = "final-scores";
        this.persist();
    }

    // Reveal the final answer (after all teams judged)
    revealFinalAnswer() {
        if (!this.state) return;
        this.state.showAnswer = true;
        this.persist();
    }

    // Select a team to judge their final answer
    selectFinalTeam(teamId: string) {
        if (!this.state) return;
        if (this.state.finalRevealed.includes(teamId)) return; // Already judged
        this.state.activeTeamId = teamId;
        this.persist();
    }

    // Judge final answer - applies wager to score
    judgeFinalAnswer(teamId: string, correct: boolean, wager: number) {
        if (!this.state) return;

        const team = this.state.teams.find(t => t.id === teamId);

        if (team) {
            if (correct) {
                team.score += wager;
            } else {
                team.score -= wager;
            }
        }

        // Store the wager for display purposes
        this.state.finalWagers[teamId] = wager;
        this.state.finalRevealed.push(teamId);
        this.state.activeTeamId = null;

        this.persist();
    }

    // ============================================
    // Timer
    // ============================================

    private startInternalTimer() {
        // Only start if not already running
        if (this.timerInterval) return;

        this.timerInterval = setInterval(() => {
            if (!this.state) return;

            // Handle timeout countdown (revealing answer)
            if (this.state.timeoutCountdown !== null && this.state.timeoutCountdown > 0) {
                this.state.timeoutCountdown--;
                this.persist();

                if (this.state.timeoutCountdown === 0) {
                    // Show answer and start reveal countdown
                    this.state.showAnswer = true;
                    this.state.timeoutCountdown = null;
                    this.state.revealCountdown = this.state.settings.answerRevealSeconds ?? 5;
                    this.persist();
                }
                return;
            }

            // Handle reveal countdown (returning to board)
            if (this.state.revealCountdown !== null && this.state.revealCountdown > 0) {
                this.state.revealCountdown--;
                this.persist();

                if (this.state.revealCountdown === 0) {
                    // Return to board
                    if (this.state.currentQuestion) {
                        const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion;
                        const question = this.state.rounds[roundIndex]?.categories[categoryIndex]?.questions[questionIndex];
                        if (question) {
                            question.isRevealed = true;
                            this.state.questionsAnswered++;
                        }
                    }

                    // Reset state and return to board
                    this.state.currentQuestion = null;
                    this.state.showAnswer = false;
                    this.state.wrongTeamIds = [];
                    this.state.dailyDoubleWager = null;
                    this.state.activeTeamId = null;
                    this.state.timeoutCountdown = null;
                    this.state.revealCountdown = null;
                    this.state.skippingQuestion = false;
                    this.state.phase = "board";
                    this.checkRoundComplete();
                    this.persist();
                }
                return;
            }

            // Handle normal timer countdown
            if (this.state.timerRunning && this.state.timerSeconds > 0) {
                this.state.timerSeconds--;
                this.persist();
            } else if (this.state.timerRunning && this.state.timerSeconds === 0) {
                // Timer expired - start timeout countdown
                this.state.timerRunning = false;

                // In question phase, handle timeout
                if (this.state.phase === "question") {
                    // Clear active team (no one can answer now)
                    this.state.activeTeamId = null;
                    // Start 5 second countdown to answer reveal
                    this.state.timeoutCountdown = 5;
                    this.persist();
                } else {
                    this.persist();
                }
            }
        }, 1000);
    }

    private stopInternalTimer() {
        if (this.timerInterval) {
            clearInterval(this.timerInterval);
            this.timerInterval = null;
        }
    }

    setTimerMax(seconds: number) {
        if (!this.state) return;
        this.state.timerMax = seconds;
        this.persist();
    }

    // Call this from moderator view only
    enableTimerControl() {
        this.startInternalTimer();
    }

    startTimer() {
        if (!this.state) return;
        this.state.timerRunning = true;
        this.state.timerSeconds = this.state.timerMax;
        this.persist();
    }

    stopTimer() {
        if (!this.state) return;
        this.state.timerRunning = false;
        this.persist();
    }

    resetTimer() {
        if (!this.state) return;
        this.state.timerSeconds = this.state.timerMax;
        this.state.timerRunning = false;
        // Also clear any timeout countdowns
        this.state.timeoutCountdown = null;
        this.state.revealCountdown = null;
        this.persist();
    }

    // ============================================
    // Helpers
    // ============================================

    get currentRound(): Round | null {
        if (!this.state) return null;
        return this.state.rounds[this.state.currentRoundIndex] ?? null;
    }

    get currentQuestionData() {
        if (!this.state?.currentQuestion) return null;
        const { roundIndex, categoryIndex, questionIndex } = this.state.currentQuestion;
        const category = this.state.rounds[roundIndex]?.categories[categoryIndex];
        const question = category?.questions[questionIndex];
        return question ? { category, question } : null;
    }

    get sortedTeams(): Team[] {
        if (!this.state) return [];
        return [...this.state.teams].sort((a, b) => b.score - a.score);
    }

    getQuestionResult(categoryIndex: number, questionIndex: number): QuestionResult | null {
        if (!this.state) return null;
        return this.state.questionResults.find(
            r => r.categoryIndex === categoryIndex && r.questionIndex === questionIndex
        ) ?? null;
    }
}

export const gameSession = new GameSessionStore();
