// retoor <retoor@molodetz.nl>

import { Component } from "./Component.js";
import { Http } from "../Http.js";
import { Format } from "../Format.js";

const TICK_MS = 1000;
const ADVANCE_DELAY_MS = 1200;
const CHECKING_LABEL = "Checking…";

export class AppQuizPlayer extends Component {
    connectedCallback() {
        this._attemptUrl = this.attr("data-attempt-url");
        this._remaining = this.intAttr("data-remaining", 0);
        this._hasLimit = this.attr("data-has-limit") === "1";
        this._status = this.attr("data-status", "in_progress");
        this._progress = this.querySelector("[data-quiz-progress]");
        this._score = this.querySelector("[data-quiz-score]");
        this._timer = this.querySelector("[data-quiz-timer]");
        this._slides = Array.from(this.querySelectorAll("[data-quiz-slide]"));
        this._bindForms();
        if (this._slides.length) this._initSlides();
        if (this._hasLimit && this._status === "in_progress") this._startTimer();
    }

    disconnectedCallback() {
        if (this._interval) clearInterval(this._interval);
        if (this._advanceTimeout) clearTimeout(this._advanceTimeout);
    }

    _initSlides() {
        const startAt = this._slides.findIndex((slide) => !slide.classList.contains("quiz-question-answered"));
        this._buildSlideNav();
        this._showSlide(startAt === -1 ? this._slides.length - 1 : startAt);
    }

    _buildSlideNav() {
        const host = this.querySelector("[data-quiz-slides]");
        if (!host) return;
        const nav = document.createElement("div");
        nav.className = "quiz-slide-nav";
        this._prevBtn = document.createElement("button");
        this._prevBtn.type = "button";
        this._prevBtn.className = "btn btn-secondary";
        this._prevBtn.textContent = "Previous";
        this._prevBtn.addEventListener("click", () => this._showSlide(this._current - 1));
        this._counter = document.createElement("span");
        this._counter.className = "quiz-slide-counter";
        this._nextBtn = document.createElement("button");
        this._nextBtn.type = "button";
        this._nextBtn.className = "btn btn-secondary";
        this._nextBtn.textContent = "Next";
        this._nextBtn.addEventListener("click", () => this._showSlide(this._current + 1));
        nav.appendChild(this._prevBtn);
        nav.appendChild(this._counter);
        nav.appendChild(this._nextBtn);
        host.before(nav);
    }

    _showSlide(index) {
        if (!this._slides.length) return;
        if (this._advanceTimeout) {
            clearTimeout(this._advanceTimeout);
            this._advanceTimeout = null;
        }
        this._current = Math.max(0, Math.min(this._slides.length - 1, index));
        this._slides.forEach((slide, i) => {
            slide.hidden = i !== this._current;
        });
        if (this._prevBtn) this._prevBtn.disabled = this._current === 0;
        if (this._nextBtn) this._nextBtn.disabled = this._current === this._slides.length - 1;
        if (this._counter) this._counter.textContent = `Question ${this._current + 1} of ${this._slides.length}`;
    }

    _bindForms() {
        this.querySelectorAll("[data-quiz-answer-form]").forEach((form) => {
            form.addEventListener("submit", (event) => {
                event.preventDefault();
                this._submit(form);
            });
        });
    }

    _startTimer() {
        this._interval = setInterval(() => {
            this._remaining = Math.max(0, this._remaining - 1);
            if (this._timer) this._timer.textContent = Format.duration(this._remaining);
            if (this._remaining === 0) {
                clearInterval(this._interval);
                window.location.reload();
            }
        }, TICK_MS);
    }

    async _submit(form) {
        const button = form.querySelector("button[type=submit]");
        const original = button ? button.textContent : "";
        if (button) {
            button.disabled = true;
            button.textContent = CHECKING_LABEL;
        }
        let payload;
        try {
            payload = await Http.sendForm(this._attemptUrl + "/answer", this._params(form));
        } catch (error) {
            if (button) {
                button.disabled = false;
                button.textContent = original;
            }
            return;
        }
        this._renderAnswer(form, payload.answer);
        this._renderHud(payload.attempt);
        this._advance(form);
    }

    _params(form) {
        const params = [];
        new FormData(form).forEach((value, key) => params.push([key, value]));
        return params;
    }

    _renderAnswer(form, answer) {
        const question = form.closest("[data-quiz-question]");
        if (!question || !answer) return;
        question.classList.add("quiz-question-answered");
        form.querySelectorAll("input, textarea, select, button").forEach((field) => {
            field.disabled = true;
        });
        const actions = form.querySelector(".quiz-answer-actions");
        if (actions) actions.remove();
        const host = question.querySelector("[data-quiz-result]");
        if (!host) return;
        host.hidden = false;
        host.innerHTML = "";
        host.appendChild(this._gradeNode(question, answer));
        if (answer.feedback) host.appendChild(this._textNode("quiz-grade-feedback", answer.feedback));
        if (answer.graded_by === "fallback") {
            host.appendChild(
                this._textNode(
                    "quiz-grade-fallback",
                    "Automatic review was unavailable, so this answer was scored on keyword overlap.",
                ),
            );
        }
    }

    _gradeNode(question, answer) {
        const points = question.querySelector(".quiz-question-points");
        const max = points ? points.textContent.trim().split(" ")[0] : "";
        const grade = document.createElement("div");
        grade.className = "quiz-grade " + (answer.is_correct ? "quiz-grade-correct" : "quiz-grade-wrong");
        const verdict = document.createElement("span");
        verdict.className = "quiz-grade-verdict";
        verdict.textContent = answer.is_correct ? "Correct" : "Not correct";
        const scored = document.createElement("span");
        scored.className = "quiz-grade-points";
        scored.textContent = `${Format.number(answer.awarded_points)} / ${max} points`;
        grade.appendChild(verdict);
        grade.appendChild(scored);
        return grade;
    }

    _textNode(className, text) {
        const node = document.createElement("p");
        node.className = className;
        node.textContent = text;
        return node;
    }

    _renderHud(attempt) {
        if (!attempt) return;
        if (this._progress) {
            this._progress.textContent = `${attempt.answered_count} / ${attempt.question_count} answered`;
        }
        if (this._score) {
            this._score.textContent = `${Format.number(attempt.score_points)} / ${attempt.max_points} points`;
        }
        this._remaining = attempt.remaining_seconds;
    }

    _advance(form) {
        const question = form.closest("[data-quiz-question]");
        if (!question || !this._slides.length) return;
        const index = this._slides.indexOf(question);
        if (index === -1 || index >= this._slides.length - 1) return;
        if (this._advanceTimeout) clearTimeout(this._advanceTimeout);
        this._advanceTimeout = setTimeout(() => this._showSlide(index + 1), ADVANCE_DELAY_MS);
    }
}

customElements.define("dp-quiz-player", AppQuizPlayer);
