<?php
/**
 * Copyright (C) 2026 Plugixa (https://plugixa.com)
 * License: GNU General Public License v2 or later
 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
 */

namespace PlugixaRecipe\Api;

// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.SlowDBQuery, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom-table data layer: table identifiers are built with $wpdb->prefix and are never user input; all request values are bound via $wpdb->prepare(); ORDER BY / LIMIT fragments come out of internal allow-list maps; content reads go through PlugixaRecipe\Cache (the static sniff cannot trace caching through the wrapper); meta_key/meta_value are dedicated plugin columns, not WP_Query.

defined( 'ABSPATH' ) || exit;

use PlugixaRecipe\Cache;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

/**
 * Favorites REST Controller
 */
class FavoritesController extends RestController {

    public function __construct() {
        parent::__construct();
        $this->rest_base = 'favorites';
    }

    public function register_routes(): void {
        register_rest_route($this->namespace, '/' . $this->rest_base, [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'get_items'],
                'permission_callback' => [$this, 'check_permission'],
                'args'                => $this->get_collection_params(),
            ],
        ]);

        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<recipe_id>\d+)', [
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'toggle_favorite'],
                'permission_callback' => [$this, 'check_permission'],
            ],
        ]);

        register_rest_route($this->namespace, '/' . $this->rest_base . '/check/(?P<recipe_id>\d+)', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'check_favorite'],
                'permission_callback' => [$this, 'check_permission'],
            ],
        ]);
    }

    /**
     * Get current user's favorite recipes.
     */
    public function get_items($request): WP_REST_Response|WP_Error {
        global $wpdb;
        $prefix  = $wpdb->prefix . 'plugixarecipe_';
        $user_id = get_current_user_id();
        $pagination = $this->get_pagination($request);
        $sort = $this->get_sort($request);

        // Count — read-through object cache; key = user id.
        $total = (int) Cache::remember('favorites', 'count|' . $user_id, function () use ($wpdb, $prefix, $user_id) {
            return (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$prefix}favorites WHERE user_id = %d",
                $user_id
            ));
        });

        $allowed_orderby = ['created_at', 'title', 'rating_average'];
        $orderby = in_array($sort['orderby'], $allowed_orderby) ? $sort['orderby'] : 'f.created_at';
        if (in_array($sort['orderby'], ['title', 'rating_average'])) {
            $orderby = 'r.' . $sort['orderby'];
        } else {
            $orderby = 'f.created_at';
        }
        $order = $sort['order'] === 'ASC' ? 'ASC' : 'DESC';

        // Read-through object cache; key = user + sort + pagination (every input that changes the result).
        $cache_key = 'list|' . $user_id . '|' . $orderby . '|' . $order . '|' . $pagination['limit'] . '|' . $pagination['offset'];
        $items = Cache::remember('favorites', $cache_key, function () use ($wpdb, $prefix, $user_id, $orderby, $order, $pagination) {
            return $wpdb->get_results($wpdb->prepare(
                "SELECT r.id, r.title, r.slug, r.description, r.image_id, r.prep_time, r.cook_time,
                        r.total_time, r.difficulty, r.rating_average, r.rating_count, r.status,
                        r.is_vegetarian, r.is_vegan, r.is_gluten_free, r.is_dairy_free,
                        f.created_at AS favorited_at
                FROM {$prefix}favorites f
                INNER JOIN {$prefix}recipes r ON f.recipe_id = r.id
                WHERE f.user_id = %d
                ORDER BY {$orderby} {$order}
                LIMIT %d OFFSET %d",
                $user_id,
                $pagination['limit'],
                $pagination['offset']
            ), ARRAY_A) ?: [];
        });

        foreach ($items as &$item) {
            if (!empty($item['image_id'])) {
                $item['image_url'] = wp_get_attachment_image_url((int) $item['image_id'], 'medium');
                $item['image_thumbnail'] = wp_get_attachment_image_url((int) $item['image_id'], 'thumbnail');
            }
            $item['rating_average'] = (float) $item['rating_average'];
            $item['rating_count'] = (int) $item['rating_count'];
            $item['is_favorited'] = true;
        }

        return $this->success([
            'items'  => $items,
            'total'  => $total,
            'limit'  => $pagination['limit'],
            'offset' => $pagination['offset'],
        ]);
    }

    /**
     * Toggle favorite status for a recipe (add/remove).
     */
    public function toggle_favorite($request): WP_REST_Response|WP_Error {
        global $wpdb;
        $prefix = $wpdb->prefix . 'plugixarecipe_';
        $user_id = get_current_user_id();
        $recipe_id = (int) $request->get_param('recipe_id');

        // Verify recipe exists
        $exists = $wpdb->get_var($wpdb->prepare("SELECT id FROM {$prefix}recipes WHERE id = %d", $recipe_id));
        if (!$exists) {
            return $this->error(__('Recipe not found.', 'plugixarecipe'), 404);
        }

        // Check if already favorited
        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$prefix}favorites WHERE user_id = %d AND recipe_id = %d",
            $user_id, $recipe_id
        ));

        if ($existing) {
            // Remove favorite
            $wpdb->delete($prefix . 'favorites', [
                'user_id'   => $user_id,
                'recipe_id' => $recipe_id,
            ]);

            // Content write: favourite status is surfaced on cached recipe reads too.
            Cache::flush('favorites', 'recipes');

            return $this->success([
                'recipe_id'    => $recipe_id,
                'is_favorited' => false,
            ], __('Recipe removed from favorites.', 'plugixarecipe'));
        } else {
            // Add favorite
            $wpdb->insert($prefix . 'favorites', [
                'user_id'    => $user_id,
                'recipe_id'  => $recipe_id,
                'created_at' => current_time('mysql'),
            ]);

            // Content write: favourite status is surfaced on cached recipe reads too.
            Cache::flush('favorites', 'recipes');

            return $this->success([
                'recipe_id'    => $recipe_id,
                'is_favorited' => true,
            ], __('Recipe added to favorites.', 'plugixarecipe'));
        }
    }

    /**
     * Check if a recipe is favorited by current user.
     */
    public function check_favorite($request): WP_REST_Response|WP_Error {
        global $wpdb;
        $prefix = $wpdb->prefix . 'plugixarecipe_';
        $user_id = get_current_user_id();
        $recipe_id = (int) $request->get_param('recipe_id');

        // Read-through object cache; key = user + recipe.
        $is_favorited = (bool) Cache::remember('favorites', 'check|' . $user_id . '|' . $recipe_id, function () use ($wpdb, $prefix, $user_id, $recipe_id) {
            return (bool) $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$prefix}favorites WHERE user_id = %d AND recipe_id = %d",
                $user_id, $recipe_id
            ));
        });

        return $this->success([
            'recipe_id'    => $recipe_id,
            'is_favorited' => $is_favorited,
        ]);
    }
}
