<?php

if (!defined('ABSPATH')) {
    exit;
}

class ACC_Runtime_REST_API_Full {

    private static string $ns = 'acc/v1';
    private static int $schema_cache_ttl = 3600;

    public static function init(): void {
        add_action('rest_api_init', [self::class, 'register_routes']);
    }

    public static function register_routes(): void {

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/schema', [
            'methods'             => 'GET',
            'callback'            => [self::class, 'get_schema'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/records', [
            'methods'             => 'GET',
            'callback'            => [self::class, 'get_records'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/record/(?P<id>\d+)', [
            'methods'             => 'GET',
            'callback'            => [self::class, 'get_record'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/record', [
            'methods'             => 'POST',
            'callback'            => [self::class, 'create_record'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/record/(?P<id>\d+)', [
            'methods'             => 'PUT',
            'callback'            => [self::class, 'update_record'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);

        register_rest_route(self::$ns, '/runtime/(?P<entity>[a-z0-9_]+)/record/(?P<id>\d+)', [
            'methods'             => 'DELETE',
            'callback'            => [self::class, 'delete_record'],
            'permission_callback' => 'acc_jwt_check_permission',
        ]);
    }

    private static function get_entity(string $entity_name) {
        global $wpdb;

        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}erp_entities WHERE entity_name=%s LIMIT 1",
                $entity_name
            ),
            ARRAY_A
        );
    }

    private static function get_fields(string $entity_name): array {
        global $wpdb;

        return $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}erp_fields WHERE entity_name=%s",
                $entity_name
            ),
            ARRAY_A
        ) ?: [];
    }

    /**
     * Validates that a table name starts with the WordPress prefix and contains only
     * safe characters, preventing injection through compromised entity records.
     */
    private static function is_safe_table_name(string $table): bool {
        global $wpdb;
        return (bool) preg_match('/^' . preg_quote($wpdb->prefix, '/') . '[a-z0-9_]+$/', $table);
    }

    public static function get_schema(WP_REST_Request $req) {
        $entity_name = sanitize_key($req['entity']);
        $cache_key   = 'runtime_schema_' . $entity_name;

        if ($cached = get_transient($cache_key)) {
            return $cached;
        }

        $entity = self::get_entity($entity_name);
        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $fields = self::get_fields($entity_name);
        $schema = ['entity' => $entity, 'fields' => $fields];

        set_transient($cache_key, $schema, self::$schema_cache_ttl);

        return $schema;
    }

    public static function get_records(WP_REST_Request $req) {
        global $wpdb;

        $entity_name = sanitize_key($req['entity']);
        $entity      = self::get_entity($entity_name);

        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $table = $entity['table_name'];
        $pk    = sanitize_key($entity['primary_key']);

        if (!self::is_safe_table_name($table)) {
            return new WP_Error('invalid_entity', 'Invalid entity configuration', ['status' => 500]);
        }

        $page   = max(1, (int) ($req->get_param('page') ?: 1));
        $size   = max(1, min(200, (int) ($req->get_param('size') ?: 10)));
        $search = trim((string) ($req->get_param('search') ?: ''));

        $where  = ' WHERE 1=1 ';
        $params = [];

        if ($search !== '') {
            $fields       = self::get_fields($entity_name);
            $search_parts = [];
            $search_like  = '%' . $wpdb->esc_like($search) . '%';

            foreach ($fields as $field) {
                $col = sanitize_key($field['field_name']);
                if ($col === '') {
                    continue;
                }
                $search_parts[] = "`{$col}` LIKE %s";
                $params[]       = $search_like;
            }

            if (!empty($search_parts)) {
                $where .= ' AND (' . implode(' OR ', $search_parts) . ')';
            }
        }

        $total_query = "SELECT COUNT(*) FROM `{$table}` {$where}";
        $total       = (int) $wpdb->get_var($params ? $wpdb->prepare($total_query, ...$params) : $total_query);

        $offset       = ($page - 1) * $size;
        $sql          = "SELECT * FROM `{$table}` {$where} ORDER BY `{$pk}` DESC LIMIT %d OFFSET %d";
        $query_params = array_merge($params, [$size, $offset]);

        $rows = $wpdb->get_results($wpdb->prepare($sql, ...$query_params), ARRAY_A);

        return [
            'data'       => $rows,
            'pagination' => [
                'total' => $total,
                'page'  => $page,
                'size'  => $size,
            ],
        ];
    }

    public static function get_record(WP_REST_Request $req) {
        global $wpdb;

        $entity = self::get_entity(sanitize_key($req['entity']));
        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $table = $entity['table_name'];
        $pk    = sanitize_key($entity['primary_key']);
        $id    = (int) $req['id'];

        if (!self::is_safe_table_name($table)) {
            return new WP_Error('invalid_entity', 'Invalid entity configuration', ['status' => 500]);
        }

        return $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE `{$pk}`=%d", $id),
            ARRAY_A
        );
    }

    public static function create_record(WP_REST_Request $req) {
        global $wpdb;

        $entity_name = sanitize_key($req['entity']);
        $entity      = self::get_entity($entity_name);

        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $table = $entity['table_name'];
        $pk    = $entity['primary_key'];

        if (!self::is_safe_table_name($table)) {
            return new WP_Error('invalid_entity', 'Invalid entity configuration', ['status' => 500]);
        }

        $fields = self::get_fields($entity_name);
        $data   = [];

        foreach ($fields as $field) {
            $name = $field['field_name'];
            if ($req->has_param($name)) {
                $data[$name] = sanitize_text_field($req->get_param($name));
            }
        }

        $wpdb->insert($table, $data);
        $id = $wpdb->insert_id;

        return $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE `{$pk}`=%d", $id),
            ARRAY_A
        );
    }

    public static function update_record(WP_REST_Request $req) {
        global $wpdb;

        $entity_name = sanitize_key($req['entity']);
        $entity      = self::get_entity($entity_name);

        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $table = $entity['table_name'];
        $pk    = $entity['primary_key'];
        $id    = (int) $req['id'];

        if (!self::is_safe_table_name($table)) {
            return new WP_Error('invalid_entity', 'Invalid entity configuration', ['status' => 500]);
        }

        $fields = self::get_fields($entity_name);
        $data   = [];

        foreach ($fields as $field) {
            $name = $field['field_name'];
            if ($req->has_param($name)) {
                $data[$name] = sanitize_text_field($req->get_param($name));
            }
        }

        $wpdb->update($table, $data, [$pk => $id]);

        return $wpdb->get_row(
            $wpdb->prepare("SELECT * FROM `{$table}` WHERE `{$pk}`=%d", $id),
            ARRAY_A
        );
    }

    public static function delete_record(WP_REST_Request $req) {
        global $wpdb;

        $entity_name = sanitize_key($req['entity']);
        $entity      = self::get_entity($entity_name);

        if (!$entity) {
            return new WP_Error('entity_not_found', 'Entity not found', ['status' => 404]);
        }

        $table = $entity['table_name'];
        $pk    = $entity['primary_key'];
        $id    = (int) $req['id'];

        if (!self::is_safe_table_name($table)) {
            return new WP_Error('invalid_entity', 'Invalid entity configuration', ['status' => 500]);
        }

        $wpdb->delete($table, [$pk => $id]);

        return ['deleted' => true, 'id' => $id];
    }
}
