<?php
/**
 * FAQ module (S478, Sean) — Storefront → FAQ manager + [titancart_faq] accordion.
 *
 * Unlimited question/answer rows in tc_faqs, grouped by a free-text category,
 * ordered by position. The shortcode renders a native <details>/<summary>
 * accordion (keyboard + screen-reader friendly, no JS required; a few lines of
 * optional JS close the siblings so one answer is open at a time), plus
 * schema.org FAQPage JSON-LD so search engines can show the Q&As.
 *
 * Own table, own REST, own repository — nothing WordPress-specific beyond the
 * shortcode registration and wp_kses_post() for the answer HTML (Rule 4).
 *
 * @package TitanCart\Modules\Faq
 */

namespace TitanCart\Modules\Faq;

use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

defined( 'ABSPATH' ) || exit;

class FaqModule {

    public static function init(): void {
        add_action( 'rest_api_init', function () {
            ( new FaqController() )->register_routes();
        } );
        add_shortcode( 'titancart_faq', [ self::class, 'render_shortcode' ] );
    }

    /**
     * [titancart_faq category="Shipping" open_first="1" schema="1"]
     *
     * category   only this group (blank = every group, each under its heading)
     * open_first open the first answer on load (default 0)
     * schema     emit FAQPage JSON-LD (default 1)
     * single     only one answer open at a time (default 1)
     */
    public static function render_shortcode( $atts = [] ): string {
        $atts = shortcode_atts( [
            'category'   => '',
            'open_first' => '0',
            'schema'     => '1',
            'single'     => '1',
        ], is_array( $atts ) ? $atts : [], 'titancart_faq' );

        $rows = ( new FaqRepository() )->get_public( (string) $atts['category'] );
        if ( empty( $rows ) ) {
            return '<div class="tc-faq tc-faq--empty">' . esc_html__( 'No questions yet.', 'titancart-lite' ) . '</div>';
        }

        // Group by category, ungrouped first, in stored order.
        $groups = [];
        foreach ( $rows as $r ) {
            $groups[ (string) $r['category'] ][] = $r;
        }
        if ( isset( $groups[''] ) ) {
            $groups = [ '' => $groups[''] ] + $groups;
        }

        $single     = '0' !== (string) $atts['single'];
        $open_first = '0' !== (string) $atts['open_first'];
        $uid        = 'tc-faq-' . wp_unique_id();
        $first      = true;

        $html = '<div class="tc-faq" id="' . esc_attr( $uid ) . '" data-single="' . ( $single ? '1' : '0' ) . '">';
        foreach ( $groups as $cat => $items ) {
            $html .= '<section class="tc-faq__group">';
            if ( '' !== $cat ) {
                $html .= '<h2 class="tc-faq__group-title">' . esc_html( $cat ) . '</h2>';
            }
            foreach ( $items as $r ) {
                $open  = ( $open_first && $first ) ? ' open' : '';
                $first = false;
                $html .= '<details class="tc-faq__item"' . $open . '>';
                $html .= '<summary class="tc-faq__q"><span class="tc-faq__q-text">' . esc_html( $r['question'] ) . '</span><span class="tc-faq__icon" aria-hidden="true"></span></summary>';
                $html .= '<div class="tc-faq__a">' . wp_kses_post( wpautop( (string) $r['answer'] ) ) . '</div>';
                $html .= '</details>';
            }
            $html .= '</section>';
        }
        $html .= '</div>';

        if ( $single ) {
            // One open at a time: closing the siblings of a newly opened item.
            $html .= '<script>(function(){var r=document.getElementById(' . wp_json_encode( $uid ) . ');if(!r)return;r.addEventListener("toggle",function(e){var d=e.target;if(!d.open||d.tagName!=="DETAILS")return;r.querySelectorAll("details[open]").forEach(function(o){if(o!==d)o.open=false;});},true);})();</script>';
        }

        if ( '0' !== (string) $atts['schema'] ) {
            $ld = [
                '@context'   => 'https://schema.org',
                '@type'      => 'FAQPage',
                'mainEntity' => array_map( function ( $r ) {
                    return [
                        '@type'          => 'Question',
                        'name'           => (string) $r['question'],
                        'acceptedAnswer' => [ '@type' => 'Answer', 'text' => wp_strip_all_tags( (string) $r['answer'] ) ],
                    ];
                }, $rows ),
            ];
            $html .= '<script type="application/ld+json">' . wp_json_encode( $ld, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) . '</script>';
        }

        return $html;
    }
}

/**
 * tc_faqs data access.
 */
class FaqRepository {

    private $db;
    private $t;

    public function __construct() {
        $this->db = \titancart_db();
        $this->t  = $this->db->table( 'faqs' );
    }

    /** Active rows, ordered — optionally one category. */
    public function get_public( string $category = '' ): array {
        if ( '' !== $category ) {
            return $this->db->getArray(
                "SELECT id, question, answer, category, position FROM {$this->t}
                 WHERE status = 'active' AND category = ?s ORDER BY position ASC, id ASC",
                $category
            ) ?: [];
        }
        return $this->db->getArray(
            "SELECT id, question, answer, category, position FROM {$this->t}
             WHERE status = 'active' ORDER BY category = '' DESC, category ASC, position ASC, id ASC"
        ) ?: [];
    }

    /** Admin list: every row (any status), optional search, ordered. */
    public function get_all( string $search = '', string $status = '' ): array {
        $where  = [ '1=1' ];
        $params = [];
        if ( '' !== $search ) {
            $like     = '%' . $this->db->escape_like( $search ) . '%';
            $where[]  = '(question LIKE ?s OR answer LIKE ?s OR category LIKE ?s)';
            $params[] = $like; $params[] = $like; $params[] = $like;
        }
        if ( in_array( $status, [ 'active', 'disabled' ], true ) ) {
            $where[]  = 'status = ?s';
            $params[] = $status;
        }
        return $this->db->getArray(
            "SELECT * FROM {$this->t} WHERE " . implode( ' AND ', $where ) . " ORDER BY category = '' DESC, category ASC, position ASC, id ASC",
            ...$params
        ) ?: [];
    }

    public function get( int $id ): ?array {
        $row = $this->db->getRow( "SELECT * FROM {$this->t} WHERE id = ?i", $id );
        return $row ?: null;
    }

    /** @return int id */
    public function save( array $data, int $id = 0 ): int {
        $row = [
            'question'   => sanitize_text_field( (string) ( $data['question'] ?? '' ) ),
            'answer'     => wp_kses_post( (string) ( $data['answer'] ?? '' ) ),
            'category'   => sanitize_text_field( (string) ( $data['category'] ?? '' ) ),
            'status'     => ( ( $data['status'] ?? 'active' ) === 'disabled' ) ? 'disabled' : 'active',
            'updated_at' => current_time( 'mysql' ),
        ];
        if ( isset( $data['position'] ) ) {
            $row['position'] = (int) $data['position'];
        }
        if ( $id > 0 ) {
            $this->db->update( 'faqs', $row, [ 'id' => $id ] );
            return $id;
        }
        if ( ! isset( $row['position'] ) ) {
            $row['position'] = (int) $this->db->getField( "SELECT COALESCE(MAX(position), 0) + 1 FROM {$this->t} WHERE category = ?s", $row['category'] );
        }
        $row['created_at'] = current_time( 'mysql' );
        $this->db->insert( 'faqs', $row );
        return (int) $this->db->lastInsertId();
    }

    public function delete( int $id ): void {
        $this->db->delete( 'faqs', [ 'id' => $id ] );
    }

    /** Persist a full order: [ id => position ] */
    public function reorder( array $positions ): void {
        foreach ( $positions as $id => $pos ) {
            $this->db->update( 'faqs', [ 'position' => (int) $pos ], [ 'id' => (int) $id ] );
        }
    }

    /** Distinct category names (for the admin picker). */
    public function categories(): array {
        return array_values( array_filter( array_map( 'strval', (array) $this->db->getColumn( "SELECT DISTINCT category FROM {$this->t} ORDER BY category ASC" ) ) ) );
    }
}

/**
 * REST: /titancart/v1/faqs
 */
class FaqController {

    private $repo;

    public function __construct() {
        $this->repo = new FaqRepository();
    }

    public function admin_permission(): bool {
        return current_user_can( 'manage_options' ) || current_user_can( 'manage_titancart' );
    }

    public function register_routes(): void {
        register_rest_route( 'titancart/v1', '/faqs', [
            [ 'methods' => WP_REST_Server::READABLE,  'callback' => [ $this, 'list_items' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'create' ],     'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );
        register_rest_route( 'titancart/v1', '/faqs/reorder', [
            [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'reorder' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );
        register_rest_route( 'titancart/v1', '/faqs/(?P<id>[\d]+)', [
            [ 'methods' => WP_REST_Server::EDITABLE,  'callback' => [ $this, 'update' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [ $this, 'delete' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );
    }

    public function list_items( WP_REST_Request $request ): WP_REST_Response {
        $rows = $this->repo->get_all(
            sanitize_text_field( (string) $request->get_param( 'search' ) ),
            sanitize_key( (string) $request->get_param( 'status' ) )
        );
        return new WP_REST_Response( [ 'faqs' => $rows, 'total' => count( $rows ), 'categories' => $this->repo->categories() ] );
    }

    public function create( WP_REST_Request $request ): WP_REST_Response {
        $body = (array) $request->get_json_params();
        if ( '' === trim( (string) ( $body['question'] ?? '' ) ) ) {
            return new WP_REST_Response( [ 'error' => 'A question is required.' ], 400 );
        }
        $id = $this->repo->save( $body );
        return new WP_REST_Response( [ 'success' => true, 'faq' => $this->repo->get( $id ) ], 201 );
    }

    public function update( WP_REST_Request $request ): WP_REST_Response {
        $id = (int) $request->get_param( 'id' );
        if ( ! $this->repo->get( $id ) ) {
            return new WP_REST_Response( [ 'error' => 'Not found.' ], 404 );
        }
        $body = (array) $request->get_json_params();
        if ( array_key_exists( 'question', $body ) && '' === trim( (string) $body['question'] ) ) {
            return new WP_REST_Response( [ 'error' => 'A question is required.' ], 400 );
        }
        $this->repo->save( array_merge( $this->repo->get( $id ), $body ), $id );
        return new WP_REST_Response( [ 'success' => true, 'faq' => $this->repo->get( $id ) ] );
    }

    public function delete( WP_REST_Request $request ): WP_REST_Response {
        $this->repo->delete( (int) $request->get_param( 'id' ) );
        return new WP_REST_Response( [ 'success' => true ] );
    }

    /** Body: { order: [id, id, id, …] } in display order → positions 1..n */
    public function reorder( WP_REST_Request $request ): WP_REST_Response {
        $order = (array) ( $request->get_json_params()['order'] ?? [] );
        $map   = [];
        foreach ( array_values( $order ) as $i => $id ) {
            $map[ (int) $id ] = $i + 1;
        }
        $this->repo->reorder( $map );
        return new WP_REST_Response( [ 'success' => true ] );
    }
}
