<?php

namespace EstatebudConnect\Controllers;

if (!defined('WPINC')) {
    die();
}

use EstatebudConnect\Plugin;
use EstatebudConnect\Configs\App;
use EstatebudConnect\Core\Log;
use EstatebudConnect\Core\Strings;
use EstatebudConnect\Core\Cache;
use EstatebudConnect\Models\Field;
use EstatebudConnect\Models\Forms;
use EstatebudConnect\Models\Project;
use EstatebudConnect\Models\Property;
use EstatebudConnect\Models\SearchExperience;
use EstatebudConnect\Models\Settings;
use EstatebudConnect\Services\Estatebud;
use EstatebudConnect\Traits\Singleton;
use EstatebudConnect\Utilities\Helper;

class Listings
{
    use Singleton;

    public function __construct()
    {
        add_action('wp_ajax_estatebud_post_filter_data', [$this, 'handle_search_request']);
        add_action('wp_ajax_nopriv_estatebud_post_filter_data', [$this, 'handle_search_request']);
        add_action('wp_ajax_estatebud_get_listing_card_markup', [$this, 'handle_get_listing_card_markup']);
        add_action('wp_ajax_nopriv_estatebud_get_listing_card_markup', [$this, 'handle_get_listing_card_markup']);
    }

    public function enqueue_assets($atts, $is_favorites = false)
    {
        wp_enqueue_style(App::SLUG . '-maps', Helper::get_url('dist/bundle-maps.css'), [], App::VERSION);

        if (Plugin::has_build_tools()) {
            wp_enqueue_script(App::SLUG . '-maps', Helper::get_url('app/Javascript/maps/index.js'), [], uniqid());
            wp_enqueue_script(App::SLUG . '-frontend', Helper::get_url('app/Javascript/public/index.js'), [], uniqid());
        } else {
            wp_enqueue_script(App::SLUG . '-maps', Helper::get_url('dist/bundle-maps.js'), [], App::VERSION);
            wp_enqueue_script(App::SLUG . '-frontend', Helper::get_url('dist/bundle-main.js'), [App::SLUG . '-maps'], App::VERSION);
        }

        $settings_data = Settings::instance()->get_data();
        $map_provider = $settings_data['map_provider'];
        $map_api_key = $settings_data['map_access_token'];
        $mapbox_style = $settings_data['mapbox_style'];
        $favorites_contact_form = $settings_data['favorites_contact_form'] ?? '';

        $search_exp_id = esc_html($atts['search']);
        $form_id = esc_html($atts['form']);
        $map = esc_html($atts['map']);
        $map_position = esc_html($atts['map_position']);
        $map_visibility = esc_html($atts['map_visibility']);
        $map_clustering = esc_html($atts['map_clustering']);
        $map_default_state = esc_html($atts['map_default_state']);
        $sorting = esc_html($atts['sorting']);
        $results_per_page = esc_html($atts['results_per_page']);
        $widget_mode = esc_html($atts['widget_mode']);
        $shortcode_id = esc_html($atts['shortcode_id']);
        $map_zoom_limit_normalized = esc_html($atts['map_zoom_limit']) ?: '10';
        $map_zoom_limit = App::NORMALIZED_MAP_ZOOM[$map_zoom_limit_normalized][$map_provider];

        $search_exp_data = SearchExperience::instance()->get_search_exp($search_exp_id);
        $favorites_attr = strtolower(trim(esc_html($atts['favorites'] ?? '')));
        $search_exp_fields = [];

        $listing_type = 'property';

        if (in_array($favorites_attr, ['property', 'project'], true)) {
            $listing_type = $favorites_attr;
        } elseif ($search_exp_data) {
            if ($search_exp_data['type'] == 'project-search') {
                $listing_type = 'project';
            }
        }

        if ($search_exp_data) {
            $separated_fields = $this->separate_search_exp_fields($search_exp_id, $form_id);
            $search_exp_fields = $separated_fields['visible'];
        }

        $url_sharing_token = sanitize_text_field(wp_unslash($_GET['t'] ?? ''));
        $is_shared_favorites = !empty($url_sharing_token);

        if ($is_shared_favorites) {
            $favorites = Helper::get_favorites_by_sharing_token($url_sharing_token) ?? [];
            $favorites_sharing_key = $url_sharing_token;
        } else {
            $user_session = Helper::get_user_session();
            $favorites = $user_session['favorites'] ?? [];
            $favorites_sharing_key = $user_session['favorites_sharing_key'] ?? [];
        }

        Strings::init_defaults();
        Strings::append(
            'map_info',
            [
                'map_provider' => $map_provider,
                'map_api_key' => $map_api_key,
                'mapbox_style' => $mapbox_style,
                'map' => $map,
                'map_position' => $map_position,
                'map_visibility' => $map_visibility,
                'map_clustering' => $map_clustering,
                'map_zoom_limit' => $map_zoom_limit,
                'map_default_state' => $map_default_state,
            ],
            $shortcode_id,
        );
        Strings::append('favorites', $favorites, $shortcode_id);
        Strings::append('favorites_sharing_token', $favorites_sharing_key, $shortcode_id);
        Strings::append('favorites_contact_form', $favorites_contact_form, $shortcode_id);
        Strings::append('is_favorites', $is_favorites, $shortcode_id);
        Strings::append('is_shared_favorites', $is_shared_favorites, $shortcode_id);
        Strings::append('listing_type', $listing_type, $shortcode_id);
        Strings::append('search_exp_id', $search_exp_id, $shortcode_id);
        Strings::append('search_exp_fields', $search_exp_fields, $shortcode_id);
        Strings::append('form_id', $form_id, $shortcode_id);
        Strings::append('sorting', $sorting, $shortcode_id);
        Strings::append('results_per_page', $results_per_page, $shortcode_id);
        Strings::append('widget_mode', $widget_mode, $shortcode_id);
        Strings::append('shortcode_id', $shortcode_id, $shortcode_id);

        Strings::complete('frontend', $shortcode_id);
    }

    public function render_shortcode($atts)
    {
        $atts = shortcode_atts(
            [
                'form' => '',
                'search' => '',
                'map' => 'false',
                'map_position' => 'top',
                'map_visibility' => 'false',
                'map_clustering' => 'true',
                'map_zoom_limit' => '',
                'map_default_state' => 'off',
                'sorting' => 'published',
                'results_per_page' => '12',
                'widget_mode' => 'false',
                'favorites' => '',
            ],
            $atts,
            'estatebud',
        );

        Log::write('Listings: Search experience shortcode configuration', $atts);

        $search_exp_id = esc_html($atts['search']);
        $form_id = esc_html($atts['form']);
        $widget_mode = esc_html($atts['widget_mode']);
        $favorites_attr = strtolower(trim(esc_html($atts['favorites'])));
        $map_position = esc_html($atts['map_position']);
        $map_default_state = esc_html($atts['map_default_state']);

        if (!empty($form_id)) {
            $form_data = Forms::instance()->get_form($form_id);

            if (!$form_data) {
                return '<div class="estatebud-form-not-found-message">Form "' .
                    esc_html($form_id) .
                    '" not found</div>';
            }
            $form_type = $form_data['type'];
        } else {
            $form_data = null;
            $form_type = null;
        }

        Log::write('Listings: Form attached to search experience', $form_data);

        if (!empty($search_exp_id)) {
            $search_exp_data = SearchExperience::instance()->get_search_exp($search_exp_id);

            if (!$search_exp_data) {
                return '<div class="estatebud-form-not-found-message">Search experience "' .
                    esc_html($search_exp_id) .
                    '" not found</div>';
            }
        }

        Log::write('Listings: Search experience data', $search_exp_data);

        $shortcode_id = uniqid();
        $atts['shortcode_id'] = $shortcode_id;

        $is_favorites = in_array($favorites_attr, ['property', 'project'], true);
        
        $this->enqueue_assets($atts, $is_favorites);

        $current_url =
            (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') .
            '://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]';
        $query_string = wp_parse_url($current_url, PHP_URL_QUERY);
        $refactored_query_params = [];
        $query_params = [];
        $page_number = 1;

        if ($query_string) {
            parse_str($query_string, $query_params);
            foreach ($query_params as $key => $value) {
                if ($key === 'ebp') {
                    $page_number = (int) $value;
                    unset($query_params[$key]);
                }
                if ($key === 'map_mode' || $key === 'polygon') {
                    unset($query_params[$key]);
                }
            }
        } else {
            $query_string = [];
        }

        $settings_data = Settings::instance()->get_data();

        $listing_type = 'property';
        if ($search_exp_data && $search_exp_data['type'] == 'project-search') {
            $listing_type = 'project';
        }
        if (in_array($favorites_attr, ['property', 'project'], true)) {
            $listing_type = $favorites_attr;
        }

        $field_options = Estatebud::instance()
            ->cache(Cache::TTL_1MO)
            ->get_fields(['component' => $listing_type]);
        $location_options = Estatebud::instance()->cache(Cache::TTL_5M)->get_property_areas();
        $form_data = null;
        if (!empty($form_id)) {
            $form_data = Forms::instance()->get_form($form_id);
        }

        ob_start();
        Helper::render_markup_from_views('Public/templates/listings.php', [
            'form_id' => $form_id,
            'search_id' => $search_exp_id,
            'query_from_link' => $query_params,
            'page_number' => $page_number,
            'widget_mode' => $widget_mode,
            'shortcode_id' => $shortcode_id,
            'is_favorites' => $is_favorites,
            'map_position' => $map_position,
            'map_default_state' => $map_default_state,
            'settings_data' => $settings_data,
            'search_exp_data' => $search_exp_data,
            'listing_type' => $listing_type,
            'field_options' => $field_options,
            'location_options' => $location_options,
            'form_data' => $form_data,
        ]);
        $html = ob_get_clean();
        return $html;
    }

    public function handle_search_request()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'estatebud-nonce')) {
            wp_send_json_error(
                [
                    'message' => 'Nonce verification failed',
                    'code' => 403,
                ],
                403,
            );
        }

        if (isset($_POST['locale']) && !empty($_POST['locale'])) {
            $locale = sanitize_text_field(wp_unslash($_POST['locale']));
            switch_to_locale($locale);
        }

        Log::event('Search experience: Filter results');
        
        $query_params = [];
        $page_number = 1;
        $is_favorites = false;
        $shortcode_id = '';

        foreach ($_POST as $key => $value) {
            $key = sanitize_text_field($key);
            $value = wp_unslash($value);

            if (!in_array($key, ['action', 'locale', 'nonce'])) {
                if (strpos($key, '-') !== false) {
                    $parsed_key = explode('-', $key);
                    $min_or_max = end($parsed_key);
                    $origin_key = $parsed_key[0];
                    $query_params[$origin_key . "[$min_or_max]"] = sanitize_text_field($value);
                } elseif (strpos($key, 'area') !== false) {
                    $query_params['area'] .= sanitize_text_field($value) . ',';
                } elseif ($key === 'ebp') {
                    $page_number = (int) $value;
                } elseif ($key === 'is_favorites') {
                    $is_favorites = true;
                } elseif ($key === 'formId') {
                    $form_id = sanitize_text_field($value);
                } elseif ($key === 'searchExpId') {
                    $search_exp_id = sanitize_text_field($value);
                } elseif ($key === 'sorting') {
                    $sorting = sanitize_text_field($value);
                } elseif ($key === 'resultsPerPage') {
                    $results_per_page = sanitize_text_field($value);
                } elseif ($key === 'widgetMode') {
                    $widget_mode = sanitize_text_field($value);
                } elseif ($key === 'shortcodeId') {
                    $shortcode_id = sanitize_text_field($value);
                } elseif ($key === 'listing_type') {
                } elseif ($key === 'id') {
                    if ($value === 'none') {
                        $query_params['id'] = 'none';
                        continue;
                    }
                    if (strpos($value, ',') !== false) {
                        $ids = array_map('sanitize_text_field', explode(',', $value));
                        $query_params['id'] = implode(',', array_filter($ids));
                    } else {
                        $query_params['id'] = sanitize_text_field($value);
                    }
                } else {
                    $query_params[$key] = sanitize_text_field($value);
                }
            }
        }

        $default_sort = 'published';
        $default_sort_type = 'desc';

        if (isset($sorting)) {
            $parsed_sorting = explode('_', $sorting);

            if (isset($parsed_sorting[0])) {
                $default_sort = $parsed_sorting[0];
            }

            if (isset($parsed_sorting[1])) {
                $default_sort_type = $parsed_sorting[1];
            }

            if (!isset($query_params['sort'])) {
                $query_params['sort'] = $default_sort;
            }

            if (!isset($query_params['sort_type'])) {
                $query_params['sort_type'] = $default_sort_type;
            }
        }

        $requested_listing_type = null;
        if ($is_favorites && isset($_POST['listing_type'])) {
            $requested_listing_type = sanitize_text_field(wp_unslash($_POST['listing_type']));
        }

        $user_session = Helper::get_user_session();
        $favorites_sharing_token = $user_session['favorites_sharing_token'];
        [$search_result, $listing_type, $page_number] = $this->get_listings_search_result(
            $search_exp_id,
            $form_id,
            $query_params,
            $page_number,
            $results_per_page,
            $requested_listing_type,
        );
        if (array_key_exists('map_mode', $query_params) && isset($search_result['results'])) {
            $geoJson = [
                'type' => 'FeatureCollection',
                'meta' => [
                    'count' => count($search_result['results']),
                ],
                'features' => [],
            ];

            $filtered_results = [];
            foreach ($search_result['results'] as $key => $listing) {
                if (count(array_keys($listing)) > 10) {
                    $filtered_results[$key] = $listing;
                }

                $geoJson['features'][] = [
                    'type' => 'Feature',
                    'geometry' => [
                        'type' => 'Point',
                        'coordinates' => [$listing['location_lng'], $listing['location_lat']],
                    ],
                    'properties' => [
                        'id' => $listing['id'],
                    ],
                ];
            }

            $search_result['results'] = $filtered_results;
        }

        $settings_data = Settings::instance()->get_data();

        ob_start();

        Helper::render_markup_from_views('/Public/partials/listings/body.php', [
            'search_result' => isset($search_result) ? $search_result : [],
            'page_number' => $page_number ?? 0,
            'listing_type' => isset($listing_type) ? $listing_type : 'property',
            'sort_query' => [
                'sort' => $query_params['sort'] ?? $default_sort,
                'sort_type' => $query_params['sort_type'] ?? $default_sort_type,
            ],
            'is_favorites' => $is_favorites,
            'sorting' => $sorting ?? 'published',
            'results_per_page' => $results_per_page ?? '12',
            'widget_mode' => $widget_mode ?? 'false',
            'favorites_sharing_token' => $favorites_sharing_token,
            'shortcode_id' => $shortcode_id,
            'settings_data' => $settings_data,
        ]);

        $html = ob_get_clean();

        if ($html) {
            wp_send_json_success(
                [
                    'message' => 'Fetched new listings',
                    'code' => 200,
                    'data' => [
                        'html' => $html,
                        'geoJson' => $geoJson ?? [],
                        'shortcodeId' => $shortcode_id,
                    ],
                ],
                200,
            );
        } else {
            wp_send_json_error(
                [
                    'message' => 'Failed to fetch new listings',
                    'code' => 400,
                ],
                400,
            );
        }
    }

    public function get_listings_search_result(
        $search_id,
        $form_id,
        $user_selected_filters,
        $page_number = 1,
        $results_per_page = '12',
        $requested_listing_type = null,
    ) {
        if (isset($user_selected_filters['id']) && $user_selected_filters['id'] === 'none') {
            return [
                [
                    'results' => [],
                    'total' => 0,
                    'page' => $page_number,
                    'per_page' => $results_per_page,
                ],
                $requested_listing_type ?? 'property',
                $page_number,
            ];
        }

        $search_exp_data = SearchExperience::instance()->get_search_exp($search_id);

        $hidden_search_exp_params = $this->get_hidden_search_exp_params($search_id, $form_id);
        $merged_filter_params = $user_selected_filters;

        foreach ($hidden_search_exp_params as $key => $value) {
            $merged_filter_params[$key] = $value;
        }
        
        foreach ($merged_filter_params as $key => $value) {
            if (preg_match('/(?:price|rent)(?:.+){3}\[(?:min|max)\]/', $key, $match)) {
                $parts = explode('[', str_replace(']', '', $match[0]));

                $currency = str_replace(['price','rent'], '', $parts[0]);
                $new_key = str_replace($currency, '', $parts[0]) . '_' . $parts[1];

                if (str_contains($new_key, 'price')) {
                    $extra_key = str_replace("price", "rent", $new_key);
                } else {
                    $extra_key = str_replace("rent", "price", $new_key);
                }

                unset($merged_filter_params[$key]);

                $merged_filter_params[$new_key] = $value;
                $merged_filter_params[$extra_key] = $value;
            }
        }

        if (isset($merged_filter_params['custom_id'])) {
            $merged_filter_params['property_id'] = $merged_filter_params['custom_id'];
            unset($merged_filter_params['custom_id']);
        }

        if (isset($merged_filter_params['area[1]'])) {
            $merged_filter_params['country'] = $merged_filter_params['area[1]'];
        }

        $areas = array_filter(
            explode(
                ',',
                ($merged_filter_params['area[2]'] ?? '') . ',' .
                ($merged_filter_params['area[3]'] ?? '') . ',' .
                ($merged_filter_params['area[4]'] ?? ''),
            ),
        );

        unset(
            $merged_filter_params['area[1]'],
            $merged_filter_params['area[2]'],
            $merged_filter_params['area[3]'],
            $merged_filter_params['area[4]'],
        );

        if (count($areas)) {
            $area_options = Estatebud::instance()->cache(Cache::TTL_5M)->get_property_areas();
            $area_options_flattened = Helper::flatten_areas($area_options);

            $areas_values = array_column($area_options_flattened, 'value');

            foreach ($areas as $area) {
                $area_index = array_search($area, $areas_values);

                if ($area_index !== false) {
                    $area = $area_options_flattened[$area_index];
                    $parent_index = array_search($area['parent'], $areas);

                    if ($parent_index !== false) {
                        unset($areas[$parent_index]);
                    }
                }
            }

            $merged_filter_params['area'] = implode(',', $areas);
        }

        $merged_filter_params['limit'] = ($page_number - 1) * (int) $results_per_page . ",$results_per_page";

        if (!empty($requested_listing_type) && in_array($requested_listing_type, ['property', 'project'], true)) {
            $listing_type = $requested_listing_type;
        } elseif ($search_exp_data && $search_exp_data['type'] == 'property-search') {
            $listing_type = 'property';
        } elseif ($search_exp_data && $search_exp_data['type'] == 'project-search') {
            $listing_type = 'project';
        } else {
            $listing_type = 'property';
        }
        if ($listing_type == 'project') {
            $search_result = Estatebud::instance()->get_projects($merged_filter_params);
        } else {
            $search_result = Estatebud::instance()->get_properties($merged_filter_params);
        }
        return [$search_result, $listing_type, $page_number];
    }

    public function handle_get_listing_card_markup()
    {
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'estatebud-nonce')) {
            wp_send_json_error(
                [
                    'message' => 'Nonce verification failed',
                    'code' => 403,
                ],
                403,
            );
        }

        if (isset($_POST['locale']) && !empty($_POST['locale'])) {
            $locale = sanitize_text_field(wp_unslash($_POST['locale']));
            switch_to_locale($locale);
        }

        $listing_id = sanitize_text_field(wp_unslash($_POST['listing_id'] ?? ''));
        $listing_type = sanitize_text_field(wp_unslash($_POST['listing_type'] ?? ''));

        if ($listing_type == 'project') {
            $listing = Estatebud::instance()->get_project($listing_id);
            $listing_obj = new Project($listing);
        } else {
            $listing = Estatebud::instance()->get_property($listing_id);
            $listing_obj = new Property($listing);
        }

        $is_favorite = false;

        $user_session = Helper::get_user_session();
        $favorites = $user_session['favorites'] ?? [];

        if (in_array($listing_id, $favorites[$listing_type] ?? [])) {
            $is_favorite = true;
        }

        $settings_data = Settings::instance()->get_data();

        ob_start();

        Helper::render_markup_from_views('Public/partials/components/listing-card.php', [
            'listing' => $listing_obj,
            'is_favorite' => $is_favorite,
            'settings_data' => $settings_data,
        ]);

        $html = ob_get_clean();

        if ($html) {
            wp_send_json_success(
                [
                    'message' => 'Listing card rendered successfully',
                    'code' => 200,
                    'data' => [
                        'html' => $html,
                    ],
                ],
                200,
            );
        } else {
            wp_send_json_error(
                [
                    'message' => 'Failed to render listing card',
                    'code' => 400,
                ],
                400,
            );
        }
    }

    private function separate_search_exp_fields($search_exp_id, $form_id)
    {
        $result = [
            'visible' => [],
            'hidden' => [],
            'hidden_params' => [],
        ];

        if (empty($search_exp_id)) {
            return $result;
        }

        $search_exp_data = SearchExperience::instance()->get_search_exp($search_exp_id);
        $form_data = Forms::instance()->get_form($form_id);

        if (!$search_exp_data || !isset($search_exp_data['fields'])) {
            return $result;
        }
        if (empty($search_exp_data['fields'])) {
            return $result;
        }

        $parsed_search_exp_fields = $search_exp_data['fields'];
        $parsed_form_fields = $form_data['fields'];
        $form_field_names = [];
        foreach ($parsed_form_fields as $form_field) {
            $field_obj = new Field($form_field);
            $field_name = $field_obj->get_name();
            if (!empty($field_name)) {
                $form_field_names[] = $field_name;
            }
        }

        foreach ($parsed_search_exp_fields as $search_exp_field) {
            $search_exp_field_obj = new Field($search_exp_field);
            $search_exp_name = $search_exp_field_obj->get_name();

            $matches_by_name = !empty($search_exp_name) && in_array($search_exp_name, $form_field_names);

            if ($matches_by_name) {
                $result['visible'][] = $search_exp_field;
            } else {
                $result['hidden'][] = $search_exp_field;

                $this->convert_field_to_params($search_exp_field, $result['hidden_params']);
            }
        }

        if (isset($result['hidden_params']['area'])) {
            $result['hidden_params']['area'] = rtrim($result['hidden_params']['area'], ',');
        }

        return $result;
    }

    private function convert_field_to_params($field, &$params)
    {
        $field_obj = new Field($field);
        $field_type = $field_obj->get_type();
        $field_name = $field_obj->get_name();

        $field_value = $field_obj->get_value();
        if ($field_value !== null && $field_value !== '') {
            $is_area_field = $field_obj->is_type('select') && in_array($field_name, ['area', 'area[1]', 'area[2]', 'area[3]', 'area[4]', 'country']);
            if ($is_area_field) {
                if (!isset($params['area'])) {
                    $params['area'] = '';
                }
                $value_str = is_array($field_value) ? implode(',', $field_value) : (string) $field_value;
                $params['area'] .= $value_str . ',';
            } else {
                $param_key = !empty($field_name) ? $field_name : $field_type;
                $params[$param_key] = is_array($field_value) ? implode(',', $field_value) : $field_value;
            }
        } else {
            $min_value = $field_obj->get_min_value();
            $max_value = $field_obj->get_max_value();
            if (!empty($min_value) || !empty($max_value)) {
                $param_key = !empty($field_name) ? $field_name : $field_type;
                if (!empty($min_value)) {
                    $params[$param_key . '[min]'] = $min_value;
                }
                if (!empty($max_value)) {
                    $params[$param_key . '[max]'] = $max_value;
                }
            }
        }
    }

    private function get_hidden_search_exp_params($search_exp_id, $form_id)
    {
        $separated = $this->separate_search_exp_fields($search_exp_id, $form_id);
        return $separated['hidden_params'];
    }
}
