<?php

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

/** 
 * Checks the nonce for security and validates the action.
 * 
 * @param string|null $nonce The nonce to verify.
 * @param string $action The action name to verify against the nonce.
 * 
 * @return void Exits and sends an error if the nonce is invalid.
 */
function irep_check_nonce(?string $nonce, string $action): void
{
    if (!(isset($nonce) && wp_verify_nonce($nonce, $action))) {
        wp_send_json_error('Invalid nonce');
        exit;
    }
}

/** 
 * Sends a JSON response based on success or failure.
 * 
 * @param bool $success Indicates whether the operation was successful.
 * @param mixed $message The message to return in the response.
 * 
 * @return void Sends a JSON success or error response.
 */
function irep_send_json_response(bool $success, $message): void
{
    if ($success) {
        wp_send_json_success($message);
    } else {
        wp_send_json_error($message);
    }
}

/** 
 * Handles JSON data by encoding or decoding it.
 * 
 * @param mixed $data The data to be handled, either JSON string or an array.
 * 
 * @return mixed|null The decoded data if valid JSON, or null if there is a decoding error.
 */
function irep_handle_json_data($data)
{
    if (is_array($data)) {
        return wp_json_encode($data);
    }

    $decoded = json_decode($data, true);

    if (json_last_error() !== JSON_ERROR_NONE) {
        return null;
    }

    return $decoded;
}

/** 
 * Validates and sanitizes input data by checking for required fields and sanitizing their values.
 * 
 * @param array $data The input data to be validated and sanitized.
 * @param array $keys The keys to check in the data array.
 * @param bool $required Indicates whether the fields are mandatory.
 * 
 * @return array|null The sanitized data or null if required fields are missing or invalid.
 */
function irep_validate_and_sanitize_input(array $data, array $keys, bool $required = true): ?array
{
    $sanitized_data = [];
    foreach ($keys as $key) {
        if (array_key_exists($key, $data)) {

            // Handle different types of inputs
            if (is_string($data[$key]) && is_email($data[$key])) {
                $sanitized_data[$key] = sanitize_email($data[$key]);
            } elseif (is_string($data[$key]) && isset($data[$key]) && $data[$key] !== '') {
                $sanitized_data[$key] = sanitize_text_field($data[$key]);
            } elseif (is_numeric($data[$key]) && isset($data[$key]) && $data[$key] > 0) {
                $sanitized_data[$key] = floatval($data[$key]);
            } else if (is_bool($data[$key])) {
                $sanitized_data[$key] =  $data[$key];
            } else {
                $sanitized_data[$key] =  $data[$key] ?  $data[$key] : null;
            }


            if ((!isset($sanitized_data[$key]) || !$sanitized_data[$key]) && $required) {
                return null;  // Return null if data is required and sanitization fails
            }
        } elseif ($required) {
            return null;  // Return null if a required field is missing
        }
    }


    return $sanitized_data;
}

/** 
 * Dumps data to the error log and stops execution.
 * 
 * @param mixed $data The data to log.
 * 
 * @return void Stops execution after logging the data.
 */
function irep_dd($data): void
{
    error_log(print_r($data, true));
    die();
}

/** 
 * Retrieves detailed information about an image by its ID.
 * 
 * @param int $image_id The ID of the image.
 * 
 * @return array|null An array with image data or null if no image is found.
 */
function irep_get_image_instance(int $image_id): ?array
{
    $image_post = get_post($image_id);
    if (!$image_post) {
        return null;
    }

    $image_url = wp_get_attachment_url($image_id);
    $image_meta = wp_get_attachment_metadata($image_id);
    $image_alt = get_post_meta($image_id, '_wp_attachment_image_alt', true);
    $image_title = get_the_title($image_id);
    $image_filename = basename(get_attached_file($image_id));
    $image_author_id = $image_post->post_author;
    $image_author = get_the_author_meta('display_name', $image_author_id);
    $image_description = $image_post->post_content;
    $image_caption = $image_post->post_excerpt;
    $image_date = $image_post->post_date;
    $image_modified = $image_post->post_modified;
    $image_mime = get_post_mime_type($image_id);
    $filesize = filesize(get_attached_file($image_id));
    $filesize_human = size_format($filesize);
    $image_sizes = wp_get_attachment_metadata($image_id)['sizes'] ?? [];

    return [
        'id' => (int) $image_id,
        'title' => $image_title,
        'filename' => $image_filename,
        'url' => $image_url,
        'link' => get_permalink($image_id),
        'alt' => $image_alt,
        'author' => $image_author,
        'description' => $image_description,
        'caption' => $image_caption,
        'name' => $image_post->post_name,
        'status' => $image_post->post_status,
        'uploadedTo' => (int) $image_post->post_parent,
        'date' => $image_date,
        'modified' => $image_modified,
        'menuOrder' => (int) $image_post->menu_order,
        'mime' => $image_mime,
        'type' => 'image',
        'subtype' => strtolower(str_replace('image/', '', $image_mime)),
        'icon' => wp_mime_type_icon($image_mime),
        'dateFormatted' => get_the_date('', $image_post),
        'nonces' => [],
        'editLink' => get_edit_post_link($image_id),
        'meta' => true,
        'authorName' => get_the_author_meta('display_name', $image_author_id),
        'authorLink' => get_author_posts_url($image_author_id),
        'filesizeInBytes' => $filesize,
        'filesizeHumanReadable' => $filesize_human,
        'context' => '',
        'originalImageURL' => $image_url,
        'originalImageName' => $image_filename,
        'height' => isset($image_meta['height']) ? $image_meta['height'] : 0,
        'width'  => isset($image_meta['width']) ? $image_meta['width'] : 0,
        'orientation' => (($image_meta['height'] ?? 0) > ($image_meta['width'] ?? 0)) ? 'portrait' : 'landscape',
        'sizes' => $image_sizes,
        'compat' => [],
    ];
}

/**
 * Sanitizes a media list (image_2d / image_3d) that may contain WP media IDs or YouTube objects.
 */
function irep_sanitize_media_list(array $items): array
{
    $result = [];
    foreach ($items as $item) {
        if (is_array($item) && isset($item['type']) && $item['type'] === 'youtube' && !empty($item['url'])) {
            $result[] = ['type' => 'youtube', 'url' => sanitize_url($item['url'])];
        } elseif (is_numeric($item) && absint($item) > 0) {
            $result[] = absint($item);
        }
    }
    return $result;
}

/**
 * Resolves a single media list entry — either a WP attachment ID (int) or a YouTube object.
 */
function irep_resolve_media_item($item): ?array
{
    if (is_int($item) || (is_string($item) && ctype_digit((string) $item))) {
        return irep_get_image_instance((int) $item);
    }

    $entry = is_array($item) ? $item : (array) $item;
    if (isset($entry['type']) && $entry['type'] === 'youtube' && !empty($entry['url'])) {
        return [
            'id'          => 0,
            'url'         => sanitize_url($entry['url']),
            'mime'        => 'youtube',
            'type'        => 'youtube',
            'title'       => '',
            'filename'    => '',
            'alt'         => '',
            'author'      => '',
            'description' => '',
            'caption'     => '',
            'name'        => '',
            'status'      => '',
            'uploadedTo'  => 0,
            'date'        => '',
            'modified'    => '',
            'menuOrder'   => 0,
            'subtype'     => 'youtube',
            'icon'        => '',
            'dateFormatted' => '',
            'nonces'      => [],
            'editLink'    => '',
            'meta'        => false,
            'authorName'  => '',
            'authorLink'  => '',
            'filesizeInBytes' => 0,
            'filesizeHumanReadable' => '',
            'context'     => '',
            'originalImageURL'  => sanitize_url($entry['url']),
            'originalImageName' => '',
            'height'      => 0,
            'width'       => 0,
            'orientation' => 'landscape',
            'sizes'       => [],
            'compat'      => [],
            'link'        => '',
        ];
    }

    return null;
}

/**
 * Sanitizes sorting parameters for queries.
 * 
 * @param array $data The data containing sorting parameters.
 * @param array $allowedSortFields The list of allowed sort fields.
 * 
 * @return array The sanitized and formatted sorting parameters.
 */
function irep_sanitize_sorting_parameters(array $data, array $allowedSortFields)
{
    $allowedSortOrders = ['ASC', 'DESC'];

    $data['sort_field'] = isset($data['sort_field']) && in_array($data['sort_field'], $allowedSortFields)
        ? $data['sort_field']
        : 'id';

    $data['sort_order'] = isset($data['sort_order']) && in_array($data['sort_order'], $allowedSortOrders)
        ? $data['sort_order']
        : 'ASC';

    $data['page'] = isset($data['page']) ? intval($data['page']) : 1;
    $data['per_page'] = isset($data['per_page']) ? intval($data['per_page']) : 8;
    $data['offset'] = ($data['page'] - 1) * $data['per_page'];

    return $data;
}


/** 
 * Checks if a project ID is present and valid.
 * 
 * @param array $data The input data.
 * 
 * @return void Sends a response if project_id is missing or invalid.
 */
function irep_has_project_id(array $data)
{
    $data = irep_validate_and_sanitize_input($data, ['project_id']);

    if (!$data || $data['project_id'] < 0) {
        irep_send_json_response(false, 'project_id is Required!');
        return;
    }
}

/** 
 * Handles database duplicate errors and sends an appropriate response.
 * 
 * @param object $wpdb The WordPress database object.
 * @param string $duplicateMessage The message to send in case of a duplicate entry.
 * @param string $defaultErrorMessage The default error message if no specific error is found.
 * 
 * @return void Sends a JSON response indicating the error.
 */
function irep_database_duplicate_error($wpdb, $duplicateMessage, $defaultErrorMessage = 'Database error')
{
    if ($wpdb->last_error && strpos($wpdb->last_error, 'Duplicate entry') !== false) {
        irep_send_json_response(false, $duplicateMessage);
    } else {
        irep_send_json_response(false, $defaultErrorMessage . ': ' . $wpdb->last_error);
    }
}

/** 
 * Checks if required data fields are present and valid.
 * 
 * @param mixed $data The input data.
 * @param array $required_fields The list of required fields.
 * 
 * @return array|null The sanitized data if valid, or null if fields are missing.
 */
function irep_check_required_data($data, $required_fields)
{
    $required_data = irep_validate_and_sanitize_input($data, $required_fields, true);


    if (!$required_data) {
        irep_send_json_response(false, 'Required fields are missing.');
        return;
    }
    return $required_data;
}

/** 
 * Transforms an SVG string by cleaning and unescaping certain parts.
 * 
 * @param string $svgString The SVG content as a string.
 * 
 * @return string The transformed SVG string.
 */
function irep_transformSvgString($svgString)
{
    // Return empty string if input is null
    if ($svgString === null) {
        return '';
    }

    // Remove backslashes (only if string contains them)
    $transformedSvg = is_string($svgString) ? str_replace('\\', '', $svgString) : '';

    // Unescape HTML entities (safer approach)
    $transformedSvg = htmlspecialchars_decode($transformedSvg, ENT_QUOTES | ENT_HTML5);

    // Fix empty attributes - only process if string is not empty
    if ($transformedSvg !== '') {
        $transformedSvg = preg_replace('/(\s+[a-zA-Z0-9-]+)=""/', '$1=""', $transformedSvg);
    }

    return $transformedSvg;
}

/**
 * Sanitizes user-supplied SVG markup for safe storage and output.
 *
 * Removes executable constructs (<script>, <foreignObject>, event handlers,
 * unsafe URI schemes) and returns only the raw <svg>…</svg> element.
 * Returns an empty string if the input is not a valid SVG document.
 *
 * @param string $svg Raw SVG string from user input.
 * @return string Sanitized SVG markup, or empty string on failure.
 */
function irep_sanitize_svg(string $svg): string
{
    $svg = trim(wp_unslash($svg));
    if ($svg === '') {
        return '';
    }

    // Must contain an svg root and closing tag.
    if (!preg_match('/^\s*(?:<\?xml[^>]*>\s*)?<svg[\s\S]*<\/svg>\s*$/i', $svg)) {
        return '';
    }

    $doc = new DOMDocument('1.0', 'UTF-8');
    libxml_use_internal_errors(true);
    $loaded = $doc->loadXML($svg, LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NOBLANKS);
    libxml_clear_errors();

    if (!$loaded) {
        return '';
    }

    $root = $doc->documentElement;
    if (!$root || strtolower($root->localName) !== 'svg') {
        return '';
    }

    // Conservative allowlist aligned with icon SVGs used by the plugin.
    $allowed = [
        'svg' => [
            'xmlns',
            'xmlns:xlink',
            'viewbox',
            'width',
            'height',
            'fill',
            'stroke',
            'class',
            'role',
            'aria-hidden',
            'focusable',
            'preserveaspectratio',
        ],
        'g' => ['fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'class'],
        'path' => ['d', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'class'],
        'rect' => ['x', 'y', 'width', 'height', 'rx', 'ry', 'fill', 'stroke', 'stroke-width', 'transform', 'opacity', 'class'],
        'circle' => ['cx', 'cy', 'r', 'fill', 'stroke', 'stroke-width', 'opacity', 'class'],
        'ellipse' => ['cx', 'cy', 'rx', 'ry', 'fill', 'stroke', 'stroke-width', 'transform', 'opacity', 'class'],
        'line' => ['x1', 'y1', 'x2', 'y2', 'stroke', 'stroke-width', 'stroke-linecap', 'opacity', 'class'],
        'polyline' => ['points', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'class'],
        'polygon' => ['points', 'fill', 'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin', 'transform', 'opacity', 'class'],
    ];

    $dangerous_tags = [
        'script',
        'foreignobject',
        'iframe',
        'object',
        'embed',
        'animate',
        'animatetransform',
        'animatemotion',
        'animatecolor',
        'set',
        'handler',
        'listener',
        'style',
    ];

    $uri_attrs = ['href', 'xlink:href', 'src', 'action', 'formaction'];

    $stack = [$root];
    while (!empty($stack)) {
        $el = array_pop($stack);
        if (!($el instanceof DOMElement)) {
            continue;
        }

        $tag = strtolower($el->localName ?: $el->tagName);
        if (in_array($tag, $dangerous_tags, true) || !isset($allowed[$tag])) {
            if ($el->parentNode) {
                $el->parentNode->removeChild($el);
            }
            continue;
        }

        if ($el->hasAttributes()) {
            for ($i = $el->attributes->length - 1; $i >= 0; $i--) {
                $attr = $el->attributes->item($i);
                if (!$attr) {
                    continue;
                }

                $attr_name_raw = $attr->nodeName;
                $attr_name = strtolower($attr->localName ?: $attr_name_raw);
                $value = trim((string) $attr->nodeValue);

                // Block event handlers (onload, onclick...) and style attrs.
                if (str_starts_with($attr_name, 'on') || $attr_name === 'style') {
                    $el->removeAttribute($attr_name_raw);
                    continue;
                }

                if (!in_array($attr_name, $allowed[$tag], true)) {
                    $el->removeAttribute($attr_name_raw);
                    continue;
                }

                if (in_array($attr_name, $uri_attrs, true)) {
                    $scheme = strtolower((string) parse_url($value, PHP_URL_SCHEME));
                    if (in_array($scheme, ['javascript', 'data', 'vbscript'], true)) {
                        $el->removeAttribute($attr_name_raw);
                        continue;
                    }
                }
            }
        }

        // Iterate children; remove comments/PI and process element children.
        for ($i = $el->childNodes->length - 1; $i >= 0; $i--) {
            $child = $el->childNodes->item($i);
            if (!$child) {
                continue;
            }

            if ($child->nodeType === XML_COMMENT_NODE || $child->nodeType === XML_PI_NODE) {
                $el->removeChild($child);
                continue;
            }

            if ($child->nodeType === XML_ELEMENT_NODE) {
                $stack[] = $child;
            }
        }
    }

    $sanitized = trim((string) $doc->saveXML($root));
    if ($sanitized === '' || !preg_match('/^\s*<svg[\s\S]*<\/svg>\s*$/i', $sanitized)) {
        return '';
    }

    return $sanitized;
}


function irep_upgrade_plan()
{
    irep_send_json_response(false, 'Upgrade plan.');
}


function irep_get_translations()
{
    $translations =
        [
            'block'                 => __('block',  'interactive-real-estate-premium'),
            'floor'                 => __('floor',  'interactive-real-estate-premium'),
            'available'             => __('available',  'interactive-real-estate-premium'),
            'reserved'              => __('reserved',  'interactive-real-estate-premium'),
            'sold'                  => __('sold',  'interactive-real-estate-premium'),
            'all'                   => __('all',  'interactive-real-estate-premium'),
            'apartment'             => __('apartment',  'interactive-real-estate-premium'),
            'back'                  => __('Back',  'interactive-real-estate-premium'),
            '2d plan'               => __('2d plan',  'interactive-real-estate-premium'),
            '3d plan'               => __('3d plan',  'interactive-real-estate-premium'),
            'price'                 => __('price',  'interactive-real-estate-premium'),
            'area'                  => __('area',  'interactive-real-estate-premium'),
            'room'                  => __('room',  'interactive-real-estate-premium'),
            'starting from'         => __('starting from',  'interactive-real-estate-premium'),
            'no data available'     => __('No data available',  'interactive-real-estate-premium'),
            'request price'         => __('Request price',  'interactive-real-estate-premium'),
            'Request callback'      => __('Request callback',  'interactive-real-estate-premium'),
            'Name'                  => __('Name',  'interactive-real-estate-premium'),
            'Phone'                 => __('Phone',  'interactive-real-estate-premium'),
            'Email'                 => __('Email',  'interactive-real-estate-premium'),
            'Comment'               => __('Comment',  'interactive-real-estate-premium'),
            'Submit'                => __('Submit',  'interactive-real-estate-premium'),
            'copy'                  => __('Copy',  'interactive-real-estate-premium'),
            'copied'                => __('Copied',  'interactive-real-estate-premium'),
            'nothing found'         => __('Nothing found',  'interactive-real-estate-premium'),
            'Thank you! Your request has been received.'  => __('Thank you! Your request has been received.',  'interactive-real-estate-premium'),
            'download file'         => __('Download file',  'interactive-real-estate-premium'),
            'table view'            => __('table view',  'interactive-real-estate-premium'),
            'grid view'             => __('grid view',  'interactive-real-estate-premium'),
            'list view'             => __('list view',  'interactive-real-estate-premium'),
            'filters'               => __('filters',  'interactive-real-estate-premium'),
            'floor number'          => __('floor number',  'interactive-real-estate-premium'),
            'rooms count'           => __('rooms count',  'interactive-real-estate-premium'),
            'select config'         => __('select config',  'interactive-real-estate-premium'),
            'min'                   => __('min',  'interactive-real-estate-premium'),
            'filter'                => __('filter',  'interactive-real-estate-premium'),
            'action'                => __('action',  'interactive-real-estate-premium'),
            'any price'             => __('any price',  'interactive-real-estate-premium'),
            'any area'              => __('any area',  'interactive-real-estate-premium'),
            'any room'              => __('any room',  'interactive-real-estate-premium'),
            'any floor'             => __('any floor',  'interactive-real-estate-premium'),
            'rooms'                 => __('rooms',  'interactive-real-estate-premium'),
            'clear filters'         => __('clear filters',  'interactive-real-estate-premium'),
            'floor title'           => __('floor title',  'interactive-real-estate-premium'),
            'current'               => __('current',  'interactive-real-estate-premium'),
            'all time'              => __('all time',  'interactive-real-estate-premium'),
            'price history'         => __('price history',  'interactive-real-estate-premium'),
            'Entry'                 => __('Entry',  'interactive-real-estate-premium'),
            'available apartments'  => __('available apartments',  'interactive-real-estate-premium'),
            'apartments'            => __('apartments',  'interactive-real-estate-premium'),
            'load more'             => __('load more',  'interactive-real-estate-premium'),
            'close'                 => __('close',  'interactive-real-estate-premium'),
            'plan view'             => __('plan view',  'interactive-real-estate-premium'),
            'Slide'                 => __('Slide',  'interactive-real-estate-premium'),
            'Images'                => __('Images',  'interactive-real-estate-premium'),
            'Disable chart details' => __('Disable chart details',  'interactive-real-estate-premium'),
            'Enable chart details'  => __('Enable chart details',  'interactive-real-estate-premium'),
            'Show only filtered flats on 360' => __('Show only filtered flats on 360',  'interactive-real-estate-premium'),
            'Area'                  => __('Area',  'interactive-real-estate-premium'),
            'Floor'                 => __('Floor',  'interactive-real-estate-premium'),
            'Price'                 => __('Price',  'interactive-real-estate-premium'),
            'Rooms'                 => __('Rooms',  'interactive-real-estate-premium'),
            'Request Price'         => __('Request Price',  'interactive-real-estate-premium'),
            'Name is required'      => __('Name is required',  'interactive-real-estate-premium'),
            'Name must be at least 2 characters' => __('Name must be at least 2 characters',  'interactive-real-estate-premium'),
            'Phone is required'     => __('Phone is required',  'interactive-real-estate-premium'),
            'Email is required'     => __('Email is required',  'interactive-real-estate-premium'),
            'Enter a valid phone number' => __('Enter a valid phone number',  'interactive-real-estate-premium'),
            'Enter a valid email address' => __('Enter a valid email address',  'interactive-real-estate-premium'),
            '3d master plan'            => __('3D Master plan',  'interactive-real-estate-premium'),
            'floors'                    => __('Floors',  'interactive-real-estate-premium'),
            'no floors available'       => __('No floors available',  'interactive-real-estate-premium'),
            'no block'                  => __('No block',  'interactive-real-estate-premium'),
            'No Apartments Found'       => __('No Apartments Found', 'interactive-real-estate-premium'),
            'Try widening or clearing your filters to see more homes.' => __('Try widening or clearing your filters to see more homes.', 'interactive-real-estate-premium'),
            'View 1'                    => __('View 1', 'interactive-real-estate-premium'),
            'View'                      => __('View', 'interactive-real-estate-premium'),
        ];


    return $translations;
}


function irep_get_meta_by_key($project_id, $meta_key)
{
    $query = Irep_DB::table('irep_project_meta');

    $query->where('project_id', '=', $project_id);
    $query->where('meta_key', '=', $meta_key);

    $result = $query->get();


    return $result[0]->meta_value;
}

/**
 * Whether the Building 360 Module add-on is active and licensed.
 * The add-on itself hooks into this filter and returns the real value.
 *
 * @return bool
 */
function irep_is_building_360_addon_active(): bool
{
    return (bool) apply_filters('irep_building_360_addon_active', false);
}

/**
 * Whether the Interactive Real Estate price-history add-on is active (Freemius parent SDK).
 *
 * @return bool
 */
function irep_is_price_history_addon_active()
{
    /**
     * Do not resolve Freemius add-on instances from this plugin context.
     * Add-on lookups can fail with stale SDK path maps and break admin screens.
     *
     * Integrations can override this value if they have a reliable signal.
     */
    return (bool) apply_filters('irep_price_history_addon_active', false);
}
