<?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;

/**
 * Units REST Controller
 */
class UnitsController extends RestController {

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

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

        register_rest_route($this->namespace, '/' . $this->rest_base . '/convert', [
            [
                'methods'             => 'POST',
                'callback'            => [$this, 'convert'],
                'permission_callback' => [$this, 'check_permission'],
            ],
        ]);
    }

    /**
     * Get all available units, optionally filtered by system or type.
     */
    public function get_items($request): WP_REST_Response|WP_Error {
        global $wpdb;
        $prefix = $wpdb->prefix . 'plugixarecipe_';

        $where  = ['1=1'];
        $values = [];

        $system = $request->get_param('system');
        if ($system) {
            $where[] = 'system = %s';
            $values[] = sanitize_text_field($system);
        }

        $type = $request->get_param('type');
        if ($type) {
            $where[] = 'type = %s';
            $values[] = sanitize_text_field($type);
        }

        $where_clause = implode(' AND ', $where);

        $sql = "SELECT * FROM {$prefix}units WHERE {$where_clause} ORDER BY system ASC, type ASC, name ASC";

        $items = Cache::remember('units', 'items|' . $sql . '|' . wp_json_encode($values), function () use ($wpdb, $sql, $values) {
            return !empty($values)
                ? $wpdb->get_results($wpdb->prepare($sql, ...$values), ARRAY_A)
                : $wpdb->get_results($sql, ARRAY_A);
        });

        foreach ($items as &$item) {
            $item['id'] = (int) $item['id'];
            $item['conversion_factor'] = (float) $item['conversion_factor'];
        }

        // Group by type for convenience
        $grouped = [];
        foreach ($items as $item) {
            $grouped[$item['type']][] = $item;
        }

        return $this->success([
            'items'   => $items,
            'grouped' => $grouped,
        ]);
    }

    /**
     * Convert a quantity from one unit to another.
     *
     * Both units must share the same type (volume, weight, count).
     * Conversion uses the base unit factor: result = quantity * (from_factor / to_factor)
     */
    public function convert($request): WP_REST_Response|WP_Error {
        global $wpdb;
        $prefix = $wpdb->prefix . 'plugixarecipe_';
        $params = $request->get_json_params();

        $quantity  = isset($params['quantity']) ? (float) $params['quantity'] : null;
        $from_unit = sanitize_text_field($params['from_unit'] ?? '');
        $to_unit   = sanitize_text_field($params['to_unit'] ?? '');

        if ($quantity === null || empty($from_unit) || empty($to_unit)) {
            return $this->error(__('quantity, from_unit, and to_unit are required.', 'plugixarecipe'));
        }

        // Look up units by abbreviation or name
        $from = Cache::remember('units', "lookup:{$from_unit}", function () use ($wpdb, $prefix, $from_unit) {
            return $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$prefix}units WHERE abbreviation = %s OR name = %s LIMIT 1",
                $from_unit, $from_unit
            ), ARRAY_A);
        });

        $to = Cache::remember('units', "lookup:{$to_unit}", function () use ($wpdb, $prefix, $to_unit) {
            return $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$prefix}units WHERE abbreviation = %s OR name = %s LIMIT 1",
                $to_unit, $to_unit
            ), ARRAY_A);
        });

        if (!$from) {
            /* translators: %s: the source unit abbreviation that could not be found. */
            return $this->error(sprintf(__('Unknown unit: %s', 'plugixarecipe'), $from_unit));
        }
        if (!$to) {
            /* translators: %s: the target unit abbreviation that could not be found. */
            return $this->error(sprintf(__('Unknown unit: %s', 'plugixarecipe'), $to_unit));
        }

        // Validate same type
        if ($from['type'] !== $to['type']) {
            return $this->error(sprintf(
                /* translators: %1$s: source unit name, %2$s: source unit type, %3$s: target unit name, %4$s: target unit type. */
                __('Cannot convert between %1$s (%2$s) and %3$s (%4$s). Units must be the same type.', 'plugixarecipe'),
                $from['name'], $from['type'], $to['name'], $to['type']
            ));
        }

        $from_factor = (float) $from['conversion_factor'];
        $to_factor   = (float) $to['conversion_factor'];

        if ($to_factor == 0) {
            return $this->error(__('Invalid conversion factor.', 'plugixarecipe'));
        }

        // Convert: quantity in base units / target factor
        $converted = $quantity * ($from_factor / $to_factor);

        return $this->success([
            'original_quantity'  => $quantity,
            'original_unit'     => $from['abbreviation'],
            'converted_quantity' => round($converted, 4),
            'converted_unit'    => $to['abbreviation'],
            'from'              => $from,
            'to'                => $to,
        ]);
    }
}
