<?php
/**
 * CommentsManager — business logic for site-wide comments & discussions.
 *
 * Handles three object types:
 *   - testimonial  — general site testimonials (no object_id needed)
 *   - page         — comments on CMS/content pages (object_id = WP post ID)
 *   - category     — reviews on product categories (object_id = tc_categories.id)
 *
 * Separate from ReviewManager which handles product-level reviews.
 *
 * @package TitanCart\Modules\Comments
 */

namespace TitanCart\Modules\Comments;

defined( 'ABSPATH' ) || exit;

class CommentsManager {

    // ══════════════════════════════════════════════════════════════════════════
    // LIST + FILTER
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Get paginated list of comments with optional filters.
     *
     * @param  array $params  object_type, status, rating, search, page, per_page
     * @return array{ comments: array, total: int, pages: int }
     */
    public function get_list( array $params = [] ): array {
        $db       = titancart_db();
        $page     = max( 1, (int) ( $params['page'] ?? 1 ) );
        $per_page = min( 100, max( 1, (int) ( $params['per_page'] ?? 20 ) ) );
        $offset   = ( $page - 1 ) * $per_page;

        $where  = [];
        $binds  = [];

        if ( ! empty( $params['object_type'] ) ) {
            $where[] = 'c.object_type = ?s';
            $binds[] = $params['object_type'];
        }

        if ( ! empty( $params['status'] ) ) {
            $where[] = 'c.status = ?s';
            $binds[] = $params['status'];
        }

        if ( ! empty( $params['rating'] ) ) {
            $where[] = 'c.rating = ?i';
            $binds[] = (int) $params['rating'];
        }

        if ( ! empty( $params['search'] ) ) {
            $like    = '%' . $db->escape_like( $params['search'] ) . '%';
            $where[] = '(c.author_name LIKE ?s OR c.body LIKE ?s OR c.title LIKE ?s OR c.author_email LIKE ?s)';
            $binds[] = $like;
            $binds[] = $like;
            $binds[] = $like;
            $binds[] = $like;
        }

        $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';

        $total = (int) $db->getField(
            "SELECT COUNT(*) FROM {$db->table('comments')} c {$where_sql}",
            ...$binds
        );

        $list_binds   = $binds;
        $list_binds[] = $per_page;
        $list_binds[] = $offset;

        $rows = $db->getArray(
            "SELECT c.*,
                    cu.first_name AS customer_first,
                    cu.last_name  AS customer_last,
                    cu.email      AS customer_email
             FROM {$db->table('comments')} c
             LEFT JOIN {$db->table('customers')} cu ON cu.id = c.customer_id
             {$where_sql}
             ORDER BY c.created_at DESC
             LIMIT ?i OFFSET ?i",
            ...$list_binds
        );

        // Enrich with object names.
        foreach ( $rows as &$row ) {
            $row['object_name'] = $this->get_object_name( $row['object_type'], (int) ( $row['object_id'] ?? 0 ) );
            // Use customer name if author_name is empty.
            if ( empty( $row['author_name'] ) && ! empty( $row['customer_first'] ) ) {
                $row['author_name'] = trim( $row['customer_first'] . ' ' . ( $row['customer_last'] ?? '' ) );
            }
            if ( empty( $row['author_email'] ) && ! empty( $row['customer_email'] ) ) {
                $row['author_email'] = $row['customer_email'];
            }
        }
        unset( $row );

        return [
            'comments' => $rows ?: [],
            'total'    => $total,
            'pages'    => $total > 0 ? (int) ceil( $total / $per_page ) : 1,
            'page'     => $page,
            'per_page' => $per_page,
        ];
    }

    // ══════════════════════════════════════════════════════════════════════════
    // SINGLE + CRUD
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Get a single comment by ID.
     */
    public function get( int $id ): ?array {
        $db  = titancart_db();
        $row = $db->getRow(
            "SELECT * FROM {$db->table('comments')} WHERE id = ?i LIMIT 1",
            $id
        );
        return $row ?: null;
    }

    /**
     * Create a comment.
     */
    public function create( array $data ): array {
        $db = titancart_db();

        $insert = [
            'object_type'  => $data['object_type'] ?? 'testimonial',
            'object_id'    => ! empty( $data['object_id'] ) ? (int) $data['object_id'] : null,
            'customer_id'  => ! empty( $data['customer_id'] ) ? (int) $data['customer_id'] : null,
            'author_name'  => sanitize_text_field( $data['author_name'] ?? '' ),
            'author_email' => sanitize_email( $data['author_email'] ?? '' ),
            'rating'       => isset( $data['rating'] ) ? max( 1, min( 5, (int) $data['rating'] ) ) : null,
            'title'        => sanitize_text_field( $data['title'] ?? '' ),
            'body'         => wp_kses_post( $data['body'] ?? '' ),
            'status'       => $data['status'] ?? 'pending',
            'ip_address'   => $data['ip_address'] ?? titancart_client_ip(), // WP.org R1 🅱2: validated capture.
        ];

        $db->query(
            "INSERT INTO {$db->table('comments')}
             (object_type, object_id, customer_id, author_name, author_email, rating, title, body, status, ip_address, created_at)
             VALUES (?s, ?i, ?i, ?s, ?s, ?i, ?s, ?s, ?s, ?s, ?s)",
            $insert['object_type'],
            $insert['object_id'] ?? 0,
            $insert['customer_id'] ?? 0,
            $insert['author_name'],
            $insert['author_email'],
            $insert['rating'],
            $insert['title'],
            $insert['body'],
            $insert['status'],
            $insert['ip_address'],
            titancart_now_utc() // KI-24: UTC created_at, not MySQL-tz NOW()
        );

        $id = $db->getField( "SELECT LAST_INSERT_ID()" );

        return [ 'success' => true, 'id' => (int) $id ];
    }

    /**
     * Update a comment.
     */
    public function update( int $id, array $data ): array {
        $db  = titancart_db();
        $row = $this->get( $id );
        if ( ! $row ) {
            return [ 'success' => false, 'message' => 'Comment not found.' ];
        }

        $sets   = [];
        $binds  = [];
        $fields = [
            'author_name'  => 'sanitize_text_field',
            'author_email' => 'sanitize_email',
            'title'        => 'sanitize_text_field',
            'body'         => 'wp_kses_post',
            'status'       => 'sanitize_text_field',
        ];

        foreach ( $fields as $key => $sanitizer ) {
            if ( array_key_exists( $key, $data ) ) {
                $sets[]  = "`{$key}` = ?s";
                $binds[] = $sanitizer( $data[ $key ] );
            }
        }

        if ( array_key_exists( 'rating', $data ) ) {
            $sets[]  = 'rating = ?i';
            $binds[] = $data['rating'] !== null ? max( 1, min( 5, (int) $data['rating'] ) ) : null;
        }

        if ( empty( $sets ) ) {
            return [ 'success' => true ];
        }

        $binds[] = $id;
        $db->query(
            "UPDATE {$db->table('comments')} SET " . implode( ', ', $sets ) . " WHERE id = ?i",
            ...$binds
        );

        return [ 'success' => true ];
    }

    /**
     * Delete a comment.
     */
    public function delete( int $id ): array {
        $db = titancart_db();
        $db->query( "DELETE FROM {$db->table('comments')} WHERE id = ?i", $id );
        return [ 'success' => true ];
    }

    /**
     * Update status (approve/reject).
     */
    public function set_status( int $id, string $status ): array {
        return $this->update( $id, [ 'status' => $status ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // PUBLIC STOREFRONT READER
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Get approved testimonials for public storefront display.
     *
     * Hard-filters object_type='testimonial' AND status='approved' regardless of
     * caller input. Joins tc_customers so the template can prefer the customer
     * record over author_name when the testimonial is linked to a registered
     * shopper.
     *
     * @param array $args  count (int, default 12), min_rating (int 0-5, default 0),
     *                     page (int, default 1)
     * @return array{ testimonials: array, total: int, pages: int, page: int, per_page: int }
     */
    public function get_public_testimonials( array $args = [] ): array {
        $db       = titancart_db();
        $page     = max( 1, (int) ( $args['page'] ?? 1 ) );
        $per_page = min( 100, max( 1, (int) ( $args['count'] ?? $args['per_page'] ?? 12 ) ) );
        $offset   = ( $page - 1 ) * $per_page;

        $min_rating = max( 0, min( 5, (int) ( $args['min_rating'] ?? 0 ) ) );

        $where = [
            "c.object_type = 'testimonial'",
            "c.status = 'approved'",
        ];
        $binds = [];

        if ( $min_rating > 0 ) {
            $where[] = 'c.rating >= ?i';
            $binds[] = $min_rating;
        }

        $where_sql = 'WHERE ' . implode( ' AND ', $where );

        $total = (int) $db->getField(
            "SELECT COUNT(*) FROM {$db->table('comments')} c {$where_sql}",
            ...$binds
        );

        $list_binds   = $binds;
        $list_binds[] = $per_page;
        $list_binds[] = $offset;

        $rows = $db->getArray(
            "SELECT c.id,
                    c.customer_id,
                    c.author_name,
                    c.rating,
                    c.title,
                    c.body,
                    c.created_at,
                    cu.first_name AS customer_first,
                    cu.last_name  AS customer_last
             FROM {$db->table('comments')} c
             LEFT JOIN {$db->table('customers')} cu ON cu.id = c.customer_id
             {$where_sql}
             ORDER BY c.created_at DESC
             LIMIT ?i OFFSET ?i",
            ...$list_binds
        ) ?: [];

        $result = [
            'testimonials' => $rows,
            'total'        => $total,
            'pages'        => $total > 0 ? (int) ceil( $total / $per_page ) : 1,
            'page'         => $page,
            'per_page'     => $per_page,
        ];

        /**
         * Filter the storefront testimonial list before render.
         *
         * @param array $result  Full payload — pass through or mutate the testimonials sub-array.
         * @param array $args    The args originally passed to get_public_testimonials().
         */
        return apply_filters( 'titancart_testimonials_storefront', $result, $args );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // STATS
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Get counts by object_type and status.
     */
    public function stats(): array {
        $db = titancart_db();
        $rows = $db->getArray(
            "SELECT object_type, status, COUNT(*) AS cnt
             FROM {$db->table('comments')}
             GROUP BY object_type, status"
        );

        $stats = [
            'testimonial' => [ 'pending' => 0, 'approved' => 0, 'rejected' => 0, 'total' => 0 ],
            'page'        => [ 'pending' => 0, 'approved' => 0, 'rejected' => 0, 'total' => 0 ],
            'category'    => [ 'pending' => 0, 'approved' => 0, 'rejected' => 0, 'total' => 0 ],
        ];

        foreach ( $rows as $r ) {
            $type   = $r['object_type'];
            $status = $r['status'];
            $cnt    = (int) $r['cnt'];
            if ( isset( $stats[ $type ][ $status ] ) ) {
                $stats[ $type ][ $status ] = $cnt;
                $stats[ $type ]['total']  += $cnt;
            }
        }

        $stats['grand_total'] = array_sum( array_column( $stats, 'total' ) );
        $stats['pending_total'] = ( $stats['testimonial']['pending'] ?? 0 )
                                + ( $stats['page']['pending'] ?? 0 )
                                + ( $stats['category']['pending'] ?? 0 );

        return $stats;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // HELPERS
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Resolve a human-readable name for the object a comment is attached to.
     */
    private function get_object_name( string $type, int $object_id ): string {
        if ( ! $object_id ) return '—';
        $db = titancart_db();

        switch ( $type ) {
            case 'category':
                $name = $db->getField(
                    "SELECT ct.name
                     FROM {$db->table('category_translations')} ct
                     WHERE ct.category_id = ?i AND ct.language_code = 'en'
                     LIMIT 1",
                    $object_id
                );
                return $name ?: "Category #{$object_id}";

            case 'page':
                $post = get_post( $object_id );
                return $post ? $post->post_title : "Page #{$object_id}";

            default:
                return '—';
        }
    }
}
