/**
 * TitanCart — reviews.js
 *
 * Handles three interactive behaviours on the storefront:
 *
 *   1. Star picker  — hover / click to set rating on the review form.
 *   2. Review form  — AJAX submission via REST API.
 *   3. Helpful vote — thumbs-up / thumbs-down buttons on each review.
 *
 * Depends on: titancartReviews (wp_localize_script object from ReviewsModule::enqueue_scripts)
 *   titancartReviews.restBase    — e.g. "https://example.com/wp-json/titancart/v1"
 *   titancartReviews.nonce       — wp_rest nonce
 *   titancartReviews.isLoggedIn  — bool
 *   titancartReviews.loginUrl    — redirect target when login is required
 *   titancartReviews.i18n        — translated strings
 *
 * Vanilla JavaScript, no jQuery.
 */

( function () {
    'use strict';

    const cfg = window.titancartReviews || {};
    const i18n = cfg.i18n || {};

    // ── Helpers ────────────────────────────────────────────────────────────

    /**
     * Execute a JSON REST request.
     *
     * @param {string} method   HTTP verb
     * @param {string} endpoint Path relative to titancartReviews.restBase
     * @param {object} body     Request payload (JSON-serialised)
     * @returns {Promise<object>}
     */
    async function apiRequest( method, endpoint, body = null ) {
        const options = {
            method,
            headers: {
                'Content-Type': 'application/json',
                'X-WP-Nonce':   cfg.nonce || '',
            },
            credentials: 'same-origin',
        };
        if ( body ) {
            options.body = JSON.stringify( body );
        }

        const res = await fetch( ( cfg.restBase || '' ) + endpoint, options );
        const data = res.status === 204 ? {} : await res.json();

        if ( ! res.ok ) {
            const msg = data.message || i18n.errorGeneric || 'An error occurred.';
            throw Object.assign( new Error( msg ), { code: data.code, status: res.status } );
        }

        return data;
    }

    /**
     * Obtain a reCAPTCHA token for the review form, if the review surface is
     * protected. Returns '' when unprotected so callers omit the field.
     *
     * v3 — execute inline against grecaptcha (the footer script loaded api.js).
     * v2 — read the token from the auto-rendered g-recaptcha-response field that
     *      Google injected inside the form (the widget rendered by the
     *      titancart_recaptcha_field hook).
     *
     * @param {HTMLFormElement} form
     * @returns {Promise<string>}
     */
    async function recaptchaToken( form ) {
        const rc = cfg.recaptcha;
        if ( ! rc || ! rc.siteKey ) return '';

        if ( rc.version === 'v3' ) {
            return new Promise( function ( resolve ) {
                function run() {
                    if ( window.grecaptcha && window.grecaptcha.execute ) {
                        window.grecaptcha.ready( function () {
                            window.grecaptcha.execute( rc.siteKey, { action: rc.action } )
                                .then( function ( t ) { resolve( t || '' ); } )
                                .catch( function () { resolve( '' ); } );
                        } );
                    } else {
                        setTimeout( run, 150 );
                    }
                }
                run();
            } );
        }

        // v2 — token lives in the textarea Google renders inside the form.
        const field = form.querySelector( '[name="g-recaptcha-response"]' );
        return field ? ( field.value || '' ) : '';
    }

    /**
     * Show a notice banner inside a container element.
     *
     * @param {HTMLElement} el     The .js-tc-review-notice element
     * @param {string}      text   Message text (HTML is NOT escaped — pass safe strings only)
     * @param {'info'|'success'|'error'} type
     */
    function showNotice( el, text, type = 'info' ) {
        el.className = `tc-notice tc-notice--${ type } js-tc-review-notice`;
        el.textContent = text;
        el.hidden = false;
        el.scrollIntoView( { behavior: 'smooth', block: 'nearest' } );
    }

    function hideNotice( el ) {
        el.hidden = true;
    }

    // ── 1. Star Picker ─────────────────────────────────────────────────────

    /**
     * Initialise all star pickers on the page.
     *
     * Each .js-tc-star-picker manages an array of .tc-star-picker__star buttons
     * and keeps an adjacent hidden input (.js-tc-rating-value) in sync.
     */
    function initStarPickers() {
        document.querySelectorAll( '.js-tc-star-picker' ).forEach( function ( picker ) {
            const stars  = Array.from( picker.querySelectorAll( '.tc-star-picker__star' ) );
            // S201 B9 — match either the primary rating input
            // (.js-tc-rating-value) or the marketplace vendor rating
            // input (.js-tc-vendor-rating-value). Each fieldset
            // contains exactly one of these classes (never both) so
            // a single selector with a comma matches the correct
            // hidden input for whichever picker fired.
            const input  = picker.closest( 'fieldset' )?.querySelector( '.js-tc-rating-value, .js-tc-vendor-rating-value' );
            const label  = picker.closest( 'fieldset' )?.querySelector( '.js-tc-star-label' );
            const labels = i18n.stars || {};
            let  current = 0;

            /** Colour stars up-to and including `upTo` index (1-based). */
            function paint( upTo ) {
                stars.forEach( function ( star, idx ) {
                    const filled = idx < upTo;
                    star.querySelector( 'svg' ).setAttribute( 'fill', filled ? '#D9A441' : '#E1E5EA' );
                    star.setAttribute( 'aria-checked', filled && idx === upTo - 1 ? 'true' : 'false' );
                } );
                if ( label ) {
                    label.textContent = upTo ? ( labels[ upTo ] || upTo + ' stars' ) : ( i18n.selectRating || 'Click a star to rate' );
                }
            }

            stars.forEach( function ( star ) {
                const val = parseInt( star.dataset.value, 10 );

                star.addEventListener( 'mouseenter', function () {
                    paint( val );
                } );

                star.addEventListener( 'mouseleave', function () {
                    paint( current );
                } );

                star.addEventListener( 'click', function () {
                    current = val;
                    if ( input ) input.value = val;
                    paint( current );
                } );

                // Keyboard support: Space / Enter selects the star.
                star.addEventListener( 'keydown', function ( e ) {
                    if ( e.key === ' ' || e.key === 'Enter' ) {
                        e.preventDefault();
                        current = val;
                        if ( input ) input.value = val;
                        paint( current );
                    }
                } );
            } );

            // Reset the picker state on form reset.
            picker.closest( 'form' )?.addEventListener( 'reset', function () {
                current = 0;
                if ( input ) input.value = 0;
                paint( 0 );
            } );
        } );
    }

    // ── 2. Review Form AJAX Submission ────────────────────────────────────

    function initReviewForms() {
        document.querySelectorAll( '.js-tc-review-form' ).forEach( function ( form ) {
            const notice    = form.closest( '.tc-review-form-wrap' )?.querySelector( '.js-tc-review-notice' );
            const submitBtn = form.querySelector( '.js-tc-review-submit' );

            form.addEventListener( 'submit', async function ( e ) {
                e.preventDefault();

                if ( ! cfg.isLoggedIn ) {
                    if ( cfg.loginUrl ) {
                        window.location.href = cfg.loginUrl;
                    }
                    return;
                }

                const productId = parseInt( form.dataset.productId, 10 );
                const rating    = parseInt( form.querySelector( '.js-tc-rating-value' )?.value || '0', 10 );
                const title     = form.querySelector( '[name="title"]' )?.value.trim() || '';
                const body      = form.querySelector( '[name="body"]' )?.value.trim()  || '';

                // S201 B9 — optional vendor rating from second star picker
                // (rendered by marketplace's VendorReviewFormFieldInjector
                // via the titancart_review_form_extra_fields action hook).
                // Absent on admin-product reviews and on installs without
                // the marketplace extension active.
                const vendorRating = parseInt(
                    form.querySelector( '.js-tc-vendor-rating-value' )?.value || '0',
                    10
                );

                // Client-side validation.
                if ( notice ) hideNotice( notice );

                if ( ! rating ) {
                    if ( notice ) showNotice( notice, i18n.selectRating || 'Please select a rating.', 'error' );
                    return;
                }
                if ( ! body ) {
                    if ( notice ) showNotice( notice, i18n.writeReview || 'Please write your review.', 'error' );
                    return;
                }

                // Disable button while submitting.
                if ( submitBtn ) {
                    submitBtn.disabled = true;
                    submitBtn.textContent = i18n.submitting || 'Submitting…';
                }

                try {
                    const payload = { rating, title, body };
                    if ( vendorRating > 0 ) {
                        payload.vendor_rating = vendorRating;
                    }

                    // reCAPTCHA — attach a token when the review surface is protected.
                    if ( cfg.recaptcha && cfg.recaptcha.siteKey ) {
                        const token = await recaptchaToken( form );
                        if ( ! token && cfg.recaptcha.version === 'v2' ) {
                            if ( notice ) showNotice( notice, i18n.recaptcha || 'Please complete the reCAPTCHA.', 'error' );
                            if ( submitBtn ) {
                                submitBtn.disabled    = false;
                                submitBtn.textContent = i18n.submit || 'Submit Review';
                            }
                            return;
                        }
                        payload.recaptcha_token = token;
                    }

                    await apiRequest( 'POST', `/products/${ productId }/reviews`, payload );

                    // Success.
                    form.reset();
                    form.hidden = true;

                    const successMsg = cfg.autoApprove
                        ? ( i18n.submitted    || 'Thank you for your review!' )
                        : ( i18n.pendingNote  || 'Your review is awaiting moderation.' );

                    if ( notice ) {
                        showNotice( notice, successMsg, 'success' );
                    }
                } catch ( err ) {
                    if ( notice ) {
                        showNotice( notice, err.message, 'error' );
                    }
                    if ( submitBtn ) {
                        submitBtn.disabled    = false;
                        submitBtn.textContent = i18n.submit || 'Submit Review';
                    }
                }
            } );
        } );
    }

    // ── 3. Helpful / Not-helpful Votes ────────────────────────────────────

    function initVoteButtons() {
        // Use event delegation on #tc-reviews-list so it works after AJAX-injected content too.
        document.querySelectorAll( '.tc-reviews' ).forEach( function ( section ) {
            section.addEventListener( 'click', async function ( e ) {
                const btn = e.target.closest( '.js-tc-vote' );
                if ( ! btn ) return;

                e.preventDefault();

                const reviewId = parseInt( btn.dataset.reviewId, 10 );
                const vote     = parseInt( btn.dataset.vote, 10 );    // 1 or 0

                // Disable both vote buttons for this review immediately.
                const pair = section.querySelectorAll( `.js-tc-vote[data-review-id="${ reviewId }"]` );
                pair.forEach( b => { b.disabled = true; } );

                try {
                    await apiRequest( 'POST', `/reviews/${ reviewId }/vote`, { vote } );

                    // Increment the displayed count on the clicked button.
                    const countEl = btn.querySelector( '.tc-vote-count' );
                    if ( countEl ) {
                        countEl.textContent = ( parseInt( countEl.textContent, 10 ) || 0 ) + 1;
                    }

                    btn.classList.add( 'tc-btn--active' );
                    btn.setAttribute( 'aria-pressed', 'true' );

                    // Keep other button disabled to prevent switching.
                    pair.forEach( b => { if ( b !== btn ) b.disabled = true; } );
                } catch ( err ) {
                    // Re-enable if vote failed (including already-voted).
                    pair.forEach( b => { b.disabled = false; } );

                    // Show a brief toast-style message near the buttons.
                    const footer = btn.closest( '.tc-review__footer' );
                    if ( footer ) {
                        const msg = document.createElement( 'span' );
                        msg.className   = 'tc-vote-msg tc-muted';
                        msg.textContent = err.code === 'already_voted'
                            ? ( i18n.alreadyVoted || 'You already voted.' )
                            : ( err.message || i18n.errorGeneric );
                        footer.appendChild( msg );
                        setTimeout( () => msg.remove(), 3500 );
                    }
                }
            } );
        } );
    }

    // ── 4. Sort Control ───────────────────────────────────────────────────

    /**
     * When the sort <select> changes, navigate to the same page with the
     * updated sort query parameter (simple, no AJAX required — list is
     * server-rendered).
     */
    function initSortControl() {
        document.querySelectorAll( '.js-tc-review-sort' ).forEach( function ( select ) {
            select.addEventListener( 'change', function () {
                const url = new URL( window.location.href );
                url.searchParams.set( 'reviews_sort', select.value );
                url.searchParams.delete( 'reviews_page' );   // reset page on sort change
                window.location.href = url.toString() + '#tc-reviews';
            } );
        } );
    }

    // ── Init ───────────────────────────────────────────────────────────────

    document.addEventListener( 'DOMContentLoaded', function () {
        initStarPickers();
        initReviewForms();
        initVoteButtons();
        initSortControl();
    } );

} )();
