<?php

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

/**
 * Class Irep_Block
 *
 * Handles block-related operations in the project management system.
 * Provides methods to create, update, retrieve, and delete blocks from the database.
 *
 * @package Irep_Block
 */
class Irep_Block
{
    protected $wpdb;
    protected $table_name;

    /**
     * Irep_Block constructor.
     *
     * Initializes the global $wpdb object and sets the table name.
     */
    public function __construct()
    {
        global $wpdb;
        $this->wpdb = $wpdb;
        $this->table_name = 'irep_blocks';
    }

    /**
     * Retrieves a list of blocks based on the provided parameters.
     *
     * @param array $data An associative array containing the request data, including:
     *                    - nonce (string) The nonce for security validation.
     *                    - project_id (int) The ID of the project to fetch blocks for.
     *                    - search (string, optional) A search term to filter blocks.
     *                    - sort_field (string) The field to sort the results by.
     *                    - sort_order (string) The order of the sorting (ASC or DESC).
     *                    - per_page (int) The number of items per page.
     *                    - offset (int) The offset for pagination.
     * 
     * @return array A two-element array:
     *               - bool: Success status of the operation.
     *               - array: The data, containing the block records, total count, page, and per-page values.
     */
    public function get_block($data)
    {

        $data = [
            'nonce'        => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'project_id'   => isset($data['project_id']) ? absint($data['project_id']) : 0,
            'sort_field'   => isset($data['sort_field']) ? sanitize_text_field($data['sort_field']) : '',
            'sort_order'   => isset($data['sort_order']) ? sanitize_text_field($data['sort_order']) : '',
            'page'         => isset($data['page']) ? absint($data['page']) : 1,
            'per_page'     => isset($data['per_page']) ? absint($data['per_page']) : 8,
            'search'       => isset($data['search']) ? sanitize_text_field($data['search']) : '',
        ];

        // Check nonce for security
        irep_check_nonce($data['nonce'], 'irep_nonce');
        irep_has_project_id($data);

        // Sanitize and filter sorting parameters
        $data = irep_sanitize_sorting_parameters($data, ['id', 'title', 'conf']);


        $query = Irep_DB::table($this->table_name);
        $query->where('project_id', '=', $data['project_id']);

        $searchTerm = '%' . $data['search'] . '%';

        if (!empty($data['search'])) {
            $query->where('title', 'LIKE', $searchTerm)
                ->orWhere('id', 'LIKE', $searchTerm);
        }


        $results = $query->orderBy($data['sort_field'], $data['sort_order'])
            ->paginate($data['page'], $data['per_page']);

        if (!$results) {
            return [false,  'something went wrong!'];
        } else {

            // Map the block data to a more usable format
            if ($results['data']) {
                $results['data'] = array_map([$this, 'map_block_data'], $results['data']);
            }

            return [true, $results];
        }
    }

    /**
     * Creates a new block and inserts it into the database.
     *
     * @param array $data An associative array containing the block data, including:
     *                    - nonce (string) The nonce for security validation.
     *                    - title (string) The title of the block.
     *                    - block_image (string) The URL or ID of the block's image.
     *                    - project_id (int) The ID of the associated project.
     *                    - conf (string, optional) Configuration data for the block.
     *                    - polygon_data (string, optional) The polygon data for the block.
     *                    - svg (string, optional) The SVG representation of the block.
     * 
     * @return void
     * 
     * @throws Exception If the block number already exists for the project.
     */
    public function create_block($data)
    {
        // Capture multi-view fields before $data is reassigned below.
        $block_views_raw = isset($data['block_views']) ? $data['block_views'] : null;
        $primary_view_title = isset($data['primary_view_title']) ? sanitize_text_field($data['primary_view_title']) : '';

        $data = [
            'nonce'        => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'action'       => isset($data['action']) ? sanitize_key($data['action']) : '',
            'title'        => isset($data['title']) ? sanitize_text_field($data['title']) : '',
            'conf'         => isset($data['conf']) ? sanitize_text_field($data['conf']) : '',
            'project_id'   => isset($data['project_id']) ? absint($data['project_id']) : 0,
            'block_image'  => $data['block_image'],
            'svg'          => $data['svg'],
            'polygon_data' => isset($data['polygon_data']) ? irep_handle_json_data($data['polygon_data']) : null
        ];

        $data['svg'] = isset($_POST['svg']) ? base64_decode($_POST['svg']) : '';


        // Check nonce for security
        irep_check_nonce($data['nonce'], 'irep_nonce');
        irep_has_project_id($data);

        // Ensure required fields are present
        $required_fields = ['title', 'block_image', 'project_id'];
        $required_data = irep_check_required_data($data, $required_fields);

        // Sanitize and validate non-required fields
        $non_required_fields = ['conf', 'polygon_data', 'svg'];
        $non_required_data = irep_validate_and_sanitize_input($data, $non_required_fields, false);

        // Merge required and non-required data
        $non_required_data['polygon_data'] = $data['polygon_data'] ?? null;
        $non_required_data['svg'] = !empty($data['svg']) ? $data['svg'] : '';
        $data  = array_merge($non_required_data, $required_data);

        // Multi-view fields (mirrors project_views on projects).
        $data['block_views'] = $this->normalize_block_views($block_views_raw);
        $data['primary_view_title'] = $primary_view_title;

        $block_id = Irep_DB::table($this->table_name)->create($data);

        if (!$block_id) {
            return irep_send_json_response(false, 'something went wrong!');
        } else {
            $created_block =  Irep_DB::table($this->table_name)->find($block_id);
            $transformed = $this->map_block_data($created_block);

            return irep_send_json_response(true, $transformed);
        }
    }

    /**
     * Updates an existing block with the provided data.
     *
     * @param array $data An associative array containing the updated block data, including:
     *                    - nonce (string) The nonce for security validation.
     *                    - block_id (int) The ID of the block to be updated.
     *                    - title (string) The new title of the block.
     *                    - block_image (string) The new image URL/ID.
     *                    - conf (string, optional) The new configuration data.
     *                    - polygon_data (string, optional) The new polygon data.
     *                    - svg (string, optional) The new SVG representation.
     * 
     * @return void
     * 
     * @throws Exception If the block number already exists for the project.
     */
    public function update_block($data)
    {
        // Capture multi-view fields before $data is reassigned below.
        $block_views_raw = isset($data['block_views']) ? $data['block_views'] : null;
        $primary_view_title = isset($data['primary_view_title']) ? sanitize_text_field($data['primary_view_title']) : '';

        // Mobile variant fields (mirrors the mobile columns on projects).
        $primary_mobile_image = isset($data['primary_mobile_image']) ? esc_url_raw((string) $data['primary_mobile_image']) : '';
        $mobile_svg = $this->normalize_mobile_svg($data['mobile_svg'] ?? '');
        $mobile_polygon_data = isset($data['mobile_polygon_data']) && is_array($data['mobile_polygon_data'])
            ? wp_json_encode($data['mobile_polygon_data'])
            : wp_json_encode([]);

        $data = [
            'nonce'        => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'action'       => isset($data['action']) ? sanitize_key($data['action']) : '',
            'block_id'     => isset($data['block_id']) ? absint($data['block_id']) : 0,
            'title'        => isset($data['title']) ? sanitize_text_field($data['title']) : '',
            'conf'         => isset($data['conf']) ? sanitize_text_field($data['conf']) : '',
            'project_id'   => isset($data['project_id']) ? absint($data['project_id']) : 0,
            'block_image'  => $data['block_image'],
            'svg'          => $data['svg'],
            'polygon_data' => isset($data['polygon_data']) ? irep_handle_json_data($data['polygon_data']) : null
        ];

        $data['svg'] = isset($_POST['svg']) ? base64_decode($_POST['svg']) : '';



        // Check nonce for security
        irep_check_nonce($data['nonce'], 'irep_nonce');

        // Ensure the block_id is provided
        $block_id = isset($data['block_id']) ? intval($data['block_id']) : null;
        if (!$block_id) {
            irep_send_json_response(false, 'block_id is required');
            return;
        }

        // Prepare the keys to be updated
        $keys = ['title', 'block_image', 'polygon_data', 'svg', 'conf'];
        $params = array_filter($data, function ($value, $key) use ($keys) {
            return in_array($key, $keys) && $value;
        }, ARRAY_FILTER_USE_BOTH);


        $params['conf'] = $params['conf'] ?? null;
        $params['polygon_data'] = $params['polygon_data'] ?? null;

        // Multi-view fields are always set (even when empty) so they remain editable/clearable.
        $params['block_views'] = $this->normalize_block_views($block_views_raw);
        $params['primary_view_title'] = $primary_view_title;
        $params['primary_mobile_image'] = $primary_mobile_image;
        $params['mobile_svg'] = $mobile_svg;
        $params['mobile_polygon_data'] = $mobile_polygon_data;

        $updated_block = Irep_DB::table($this->table_name)->where('id', '=',  $block_id)->update($params);

        // Handle database update errors
        if ($updated_block->last_error) {
            irep_database_duplicate_error($this->wpdb, 'Something went wrong!');
        } else {
            irep_send_json_response(true, 'Block updated successfully');
        }
    }

    /**
     * Deletes a block from the database.
     *
     * @param array $data An associative array containing:
     *                    - nonce (string) The nonce for security validation.
     *                    - block_id (int) The ID of the block to be deleted.
     * 
     * @return void
     */
    public function delete_block($data)
    {
        $data = [
            'nonce'        => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'action'       => isset($data['action']) ? sanitize_key($data['action']) : '',
            'block_id'      => isset($data['block_id']) ? absint($data['block_id']) : 0,
        ];

        // Check nonce for security
        irep_check_nonce($data['nonce'], 'irep_nonce');

        // Ensure the block_id is provided
        $block_id = isset($data['block_id']) ? intval($data['block_id']) : null;
        if (!$block_id) {
            irep_send_json_response(false, 'block_id is required');
            return;
        }

        $deleted_block = Irep_DB::table($this->table_name)->where('id', '=',  $block_id)->delete();

        // Handle delete result
        if ($deleted_block) {
            irep_send_json_response(true, 'Block deleted successfully');
        } else {
            irep_send_json_response(false, 'Database error: ' . $deleted_block->last_error);
        }
    }

    /**
     * Maps the block data to a more usable format.
     *
     * @param array $item The block data to be mapped.
     * 
     * @return array The mapped block data.
     */
    private function map_block_data($item)
    {
        if (is_object($item)) {
            $item = (array) $item;
        }


        // Process polygon data if available
        if ($item['polygon_data']) {
            $item['polygon_data'] = irep_handle_json_data($item['polygon_data']);
        }

        // Process block image and SVG
        $item['block_image'] = [irep_get_image_instance($item['block_image'])];
        $item['svg'] = irep_transformSvgString($item['svg']);
        $item['is_active'] = $item['is_active'] === '1' || $item['is_active'] === 1;

        // Decode multi-view data (mirrors project_views on projects).
        $decoded_views = json_decode((string) ($item['block_views'] ?? ''), true);
        $item['block_views'] = is_array($decoded_views) ? $decoded_views : [];
        foreach ($item['block_views'] as &$view) {
            if (!is_array($view)) {
                continue;
            }
            $view['svg'] = irep_transformSvgString($view['svg'] ?? '');
            $view['svgRef'] = null;
            $view['mobile_svg'] = irep_transformSvgString($view['mobile_svg'] ?? '');
            $view['mobile_polygon_data'] = isset($view['mobile_polygon_data']) && is_array($view['mobile_polygon_data']) ? $view['mobile_polygon_data'] : [];
            $view['mobile_svgRef'] = null;
        }
        unset($view);

        $item['primary_view_title'] = isset($item['primary_view_title']) ? (string) $item['primary_view_title'] : '';
        $item['primary_mobile_image'] = isset($item['primary_mobile_image']) ? (string) $item['primary_mobile_image'] : '';
        $item['mobile_svg'] = irep_transformSvgString($item['mobile_svg'] ?? '');
        $decoded_mobile_polygon_data = json_decode((string) ($item['mobile_polygon_data'] ?? ''), true);
        $item['mobile_polygon_data'] = is_array($decoded_mobile_polygon_data) ? $decoded_mobile_polygon_data : [];

        return $item;
    }

    /**
     * Normalizes the raw block_views payload into a JSON string for storage.
     *
     * Accepts a JSON string or array, sanitizes each view's img/title, base64-decodes
     * the per-view svg when applicable, and keeps polygon_data only when it is an array.
     *
     * @param mixed $raw The raw block_views value from the request.
     * @return string A JSON-encoded array of normalized views.
     */
    private function normalize_block_views($raw)
    {
        if (is_string($raw)) {
            $views = json_decode(wp_unslash($raw), true) ?: [];
        } elseif (is_array($raw)) {
            $views = $raw;
        } else {
            $views = [];
        }

        $normalized = [];
        foreach ($views as $view) {
            if (!is_array($view)) {
                continue;
            }
            $svg_val = isset($view['svg']) && is_string($view['svg']) ? $view['svg'] : '';
            $decoded_svg = base64_decode($svg_val, true);
            if (is_string($decoded_svg) && strpos(ltrim($decoded_svg), '<svg') === 0) {
                $svg_val = $decoded_svg;
            }
            $mobile_svg_val = isset($view['mobile_svg']) && is_string($view['mobile_svg']) ? $view['mobile_svg'] : '';
            $decoded_mobile_svg = base64_decode($mobile_svg_val, true);
            if (is_string($decoded_mobile_svg) && strpos(ltrim($decoded_mobile_svg), '<svg') === 0) {
                $mobile_svg_val = $decoded_mobile_svg;
            }
            $normalized[] = [
                'img'          => isset($view['img']) ? esc_url_raw((string) $view['img']) : '',
                'mobile_img'   => isset($view['mobile_img']) ? esc_url_raw((string) $view['mobile_img']) : '',
                'svg'          => $svg_val,
                'mobile_svg'   => $mobile_svg_val,
                'polygon_data' => isset($view['polygon_data']) && is_array($view['polygon_data']) ? $view['polygon_data'] : [],
                'mobile_polygon_data' => isset($view['mobile_polygon_data']) && is_array($view['mobile_polygon_data']) ? $view['mobile_polygon_data'] : [],
                'title'        => isset($view['title']) ? sanitize_text_field((string) $view['title']) : '',
            ];
        }

        return json_encode($normalized);
    }

    /**
     * Normalizes an incoming mobile SVG value which arrives either base64-encoded
     * (freshly drawn) or as raw `<svg>` markup (unchanged round-trip).
     *
     * @param mixed $raw The raw mobile_svg value from the request.
     * @return string The decoded/unslashed SVG markup, or an empty string.
     */
    private function normalize_mobile_svg($raw)
    {
        $raw = is_string($raw) ? $raw : '';
        $decoded = base64_decode($raw, true);
        if (is_string($decoded) && strpos(ltrim($decoded), '<svg') === 0) {
            return $decoded;
        }
        if (strpos(ltrim($raw), '<svg') === 0) {
            return wp_unslash($raw);
        }
        return '';
    }

    /**
     * Changes the active status of a block.
     *
     * @param array $data Contains nonce, block_id, and is_active.
     */
    public function change_block_status($data)
    {
        $data = [
            'nonce'     => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'block_id'  => isset($data['block_id']) ? absint($data['block_id']) : 0,
            'is_active' => isset($data['is_active']) && rest_sanitize_boolean($data['is_active']),
        ];

        irep_check_nonce($data['nonce'], 'irep_nonce');

        $block_id = $data['block_id'];
        if (!$block_id) {
            irep_send_json_response(false, 'block_id is required');
            return;
        }

        $updated = Irep_DB::table($this->table_name)
            ->where('id', '=', $block_id)
            ->update(['is_active' => $data['is_active'] ? 1 : 0]);

        if ($updated->last_error) {
            irep_send_json_response(false, 'something went wrong!');
        } else {
            irep_send_json_response(true, 'Block status changed');
        }
    }
}

// Initialize the class
$irep_block = new Irep_Block();

// Action functions

/**
 * Retrieves blocks based on the current request and sends the response.
 *
 * Handles the `wp_ajax_irep_get_blocks` AJAX request.
 */
function irep_get_blocks()
{
    global $irep_block;
    $results = $irep_block->get_block($_POST);

    if (!$results[0]) {
        irep_send_json_response(false, $results[1]);
    } else {
        irep_send_json_response(true, $results[1]);
    }
}

/**
 * Creates a new block based on the current request and sends the response.
 *
 * Handles the `wp_ajax_irep_create_block` AJAX request.
 */
function irep_create_block()
{
    global $irep_block;
    $irep_block->create_block($_POST);
}

/**
 * Updates an existing block based on the current request and sends the response.
 *
 * Handles the `wp_ajax_irep_update_block` AJAX request.
 */
function irep_update_block()
{
    global $irep_block;
    $irep_block->update_block($_POST);
}

/**
 * Deletes a block based on the current request and sends the response.
 *
 * Handles the `wp_ajax_irep_delete_block` AJAX request.
 */
function irep_delete_block()
{
    global $irep_block;
    $irep_block->delete_block($_POST);
}

function irep_change_block_status()
{
    global $irep_block;
    $irep_block->change_block_status($_POST);
}

// Add action hooks
add_action('wp_ajax_irep_get_blocks', 'irep_get_blocks');
add_action('wp_ajax_irep_create_block', 'irep_create_block');
add_action('wp_ajax_irep_update_block', 'irep_update_block');
add_action('wp_ajax_irep_delete_block', 'irep_delete_block');
add_action('wp_ajax_irep_change_block_status', 'irep_change_block_status');
