<?php

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


class Irep_Import_Export
{


    public function export_data(array $data)
    {
        if (!$this->can_import_or_export()) {
            irep_upgrade_plan();
        }

        $data = [
            'nonce'        => isset($data['nonce']) ? sanitize_text_field($data['nonce']) : '',
            'action'       => isset($data['action']) ? sanitize_key($data['action']) : '',
            'project_id'   => isset($data['project_id']) ? absint($data['project_id']) : 0,
        ];

        $project_id = $data['project_id'];

        if (!$project_id) {
            irep_send_json_response(false, 'project_id is required');
            return;
        }

        $project = Irep_DB::table("irep_projects")->find($project_id);
        $blocks = Irep_DB::table("irep_blocks")->where('project_id', '=', $project_id)->get();
        $floors = Irep_DB::table("irep_floors")->where('project_id', '=', $project_id)->get();
        $types = Irep_DB::table("irep_types")->where('project_id', '=', $project_id)->get();
        $flats = Irep_DB::table("irep_flats")->where('project_id', '=', $project_id)->get();
        $meta = Irep_DB::table("irep_project_meta")->where('project_id', '=', $project_id)->get();
        $tooltip = Irep_DB::table("irep_tooltip")->where('project_id', '=', $project_id)->get();

        $export_data = [
            'irep_project' => $project,
            'irep_blocks' => $blocks,
            'irep_floors' => $floors,
            'irep_types' => $types,
            'irep_flats' => $flats,
            'irep_project_meta' => $meta,
            'irep_tooltip' => $tooltip,
        ];

        irep_send_json_response(true, $export_data);
    }

    public function export_zip(array $data)
    {
        if (!$this->can_import_or_export()) {
            irep_upgrade_plan();
        }

        $project_id = isset($data['project_id']) ? absint($data['project_id']) : 0;

        if (!$project_id) {
            irep_send_json_response(false, 'project_id is required');
            return;
        }

        $project = Irep_DB::table("irep_projects")->find($project_id);
        $blocks   = Irep_DB::table("irep_blocks")->where('project_id', '=', $project_id)->get();
        $floors   = Irep_DB::table("irep_floors")->where('project_id', '=', $project_id)->get();
        $types    = Irep_DB::table("irep_types")->where('project_id', '=', $project_id)->get();
        $flats    = Irep_DB::table("irep_flats")->where('project_id', '=', $project_id)->get();
        $meta     = Irep_DB::table("irep_project_meta")->where('project_id', '=', $project_id)->get();
        $tooltip  = Irep_DB::table("irep_tooltip")->where('project_id', '=', $project_id)->get();

        $export_data = [
            'irep_project'      => $project,
            'irep_blocks'       => $blocks,
            'irep_floors'       => $floors,
            'irep_types'        => $types,
            'irep_flats'        => $flats,
            'irep_project_meta' => $meta,
            'irep_tooltip'      => $tooltip,
        ];

        $image_data        = $this->collect_image_data($export_data);
        $image_urls        = $image_data['urls'];
        $attachment_id_map = $image_data['id_map'];
        $urls_360          = $image_data['urls_360'];
        $upload_dir = wp_upload_dir();
        $image_map  = [];
        $used_paths = [];

        foreach ($image_urls as $url) {
            $file_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $url);
            if (!file_exists($file_path)) {
                continue;
            }
            $basename  = basename(parse_url($url, PHP_URL_PATH));
            $zip_entry = 'images/' . $basename;
            if (in_array($zip_entry, $used_paths, true)) {
                $zip_entry = 'images/' . substr(md5($url), 0, 8) . '_' . $basename;
            }
            $used_paths[]       = $zip_entry;
            $image_map[$url]    = $zip_entry;
        }

        $zip_path = sys_get_temp_dir() . '/irep_export_' . $project_id . '_' . uniqid() . '.zip';
        $zip      = new ZipArchive();

        if ($zip->open($zip_path, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
            irep_send_json_response(false, 'Failed to create ZIP file.');
            return;
        }

        $zip->addFromString('project_data.json', wp_json_encode($export_data));
        $zip->addFromString('image_map.json', wp_json_encode($image_map));
        $zip->addFromString('attachment_id_map.json', wp_json_encode($attachment_id_map));
        $zip->addFromString('360_urls.json', wp_json_encode(array_values($urls_360)));

        foreach ($image_map as $url => $zip_entry) {
            $file_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $url);
            $zip->addFile($file_path, $zip_entry);
        }

        $zip->close();

        while (ob_get_level()) {
            ob_end_clean();
        }

        $filename = 'project_' . $project_id . '.zip';
        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename="' . $filename . '"');
        header('Content-Length: ' . filesize($zip_path));
        header('Pragma: public');
        header('Cache-Control: must-revalidate');
        readfile($zip_path);
        unlink($zip_path);
        exit;
    }

    public function import_data(array $data)
    {

        if (!$this->can_import_or_export()) {
            irep_upgrade_plan();
        }

        // ZIP branch
        if (!empty($_FILES['file']['tmp_name'])) {
            $zip_data = $this->extract_zip_data($_FILES['file']['tmp_name']);
            if (is_string($zip_data)) {
                irep_send_json_response(false, $zip_data);
                return;
            }
            $data = $zip_data;
        } else {
            // Legacy JSON branch
            $payload = isset($data['data']) ? $data['data'] : null;

            if (is_string($payload)) {
                $decoded = json_decode(wp_unslash($payload), true);
                if (!is_array($decoded)) {
                    irep_send_json_response(false, 'Invalid import JSON.');
                    return;
                }
                $data = $decoded;
            } else {
                $data = is_array($payload) ? $payload : [];
            }

            if (isset($data['data'], $data['success']) && is_array($data['data']) && !isset($data['irep_project'])) {
                $data = $data['data'];
            }
        }

        if (!isset($data['irep_project'])) {
            irep_send_json_response(false, 'Import file is missing project data.');
            return;
        }

        $project = $data['irep_project'];
        $project['title'] = $project['title'] . ' IMPORTED';

        $addon_360_active = irep_is_building_360_addon_active();

        if (!$addon_360_active) {
            unset($project['360images']);
        }

        $project = $this->prepare_polygon_svg_data($project, ['svg', 'polygon_data']);

        $project_id = Irep_DB::table('irep_projects')->create($project);
        $id_mapping = [];


        foreach (['irep_blocks', 'irep_floors'] as $table) {
            if (empty($data[$table]) || !is_array($data[$table])) continue;
            foreach ($data[$table] as $row) {
                if (empty($row)) continue;
                $row    = (array) $row;
                $old_id = $row['id'];

                $row['project_id']   = $project_id;
                $row['polygon_data'] = $row['polygon_data'] ?? '[]';

                if (!$addon_360_active) {
                    unset($row['360images']);
                }

                $row = $this->prepare_polygon_svg_data($row, ['svg', 'polygon_data']);

                if ($table === 'irep_floors') {
                    $row['block_id'] = !empty($row['block_id'])
                        ? ($id_mapping['irep_blocks'][$row['block_id']] ?? null)
                        : null;

                    $row = $this->filter_table_row(
                        $row,
                        ['floor_number', 'title', 'conf', 'floor_image', 'polygon_data', 'svg', 'project_id', 'block_id']
                    );
                } else {
                    $row = $this->filter_table_row(
                        $row,
                        ['is_active', 'title', 'conf', '360images', 'block_image', 'project_id', 'polygon_data', 'svg']
                    );
                }

                $inserted_id              = Irep_DB::table($table)->create($row);
                $id_mapping[$table][$old_id] = $inserted_id;
            }
        }

        foreach (($data['irep_types'] ?? []) as $row) {
            if (empty($row)) continue;
            $row    = (array) $row;
            $old_id = $row['id'];

            $row['project_id'] = $project_id;
            $row = $this->prepare_polygon_svg_data($row);

            foreach (['image_2d', 'image_3d', 'gallery', 'other'] as $json_field) {
                if (array_key_exists($json_field, $row)) {
                    $row[$json_field] = $this->normalize_stored_json_field($row[$json_field]);
                }
            }

            $row = $this->filter_table_row(
                $row,
                ['title', 'teaser', 'project_id', 'image_2d', 'image_3d', 'gallery', 'area_m2', 'rooms_count', 'other']
            );

            $inserted_id                       = Irep_DB::table('irep_types')->create($row);
            $id_mapping['irep_types'][$old_id] = $inserted_id;
        }

        foreach (($data['irep_flats'] ?? []) as $row) {
            if (empty($row)) continue;
            $row    = (array) $row;
            $old_id = $row['id'];

            $row['project_id'] = $project_id;
            $row['type_id']    = !empty($row['type_id'])
                ? ($id_mapping['irep_types'][$row['type_id']] ?? null)
                : null;
            $row['block_id']   = !empty($row['block_id'])
                ? ($id_mapping['irep_blocks'][$row['block_id']] ?? null)
                : null;
            $row['floor_id']   = !empty($row['floor_id'])
                ? ($id_mapping['irep_floors'][$row['floor_id']] ?? null)
                : null;

            $row = $this->prepare_polygon_svg_data($row);

            foreach (['type', 'follow_link', 'files'] as $json_field) {
                if (array_key_exists($json_field, $row)) {
                    $row[$json_field] = $this->normalize_stored_json_field($row[$json_field]);
                }
            }

            $row = $this->filter_table_row(
                $row,
                [
                    'is_active', 'block_id', 'floor_id', 'type_id', 'project_id',
                    'conf', 'flat_number', 'click_action', 'follow_link',
                    'request_price', 'price', 'offer_price', 'use_type', 'type', 'files',
                ]
            );

            $inserted_id                      = Irep_DB::table('irep_flats')->create($row);
            $id_mapping['irep_flats'][$old_id] = $inserted_id;
        }


        foreach (($data['irep_tooltip'] ?? []) as $row) {
            if (empty($row)) continue;
            $row    = (array) $row;
            $old_id = $row['id'];

            $row['project_id'] = $project_id;
            $row = $this->prepare_polygon_svg_data($row, ['data']);

            $inserted_id                        = Irep_DB::table('irep_tooltip')->create($row);
            $id_mapping['irep_tooltip'][$old_id] = $inserted_id;
        }

        foreach (($data['irep_project_meta'] ?? []) as $row) {
            if (empty($row)) continue;
            $row = (array) $row;

            $row['project_id'] = $project_id;
            $row = $this->prepare_polygon_svg_data($row);

            Irep_DB::table('irep_project_meta')->create($row);
        }


        if (isset($project['polygon_data'])) {
            $array = irep_handle_json_data($project['polygon_data']);

            $array = $this->fix_polygon_data($array, $id_mapping);

            $array = irep_handle_json_data($array);

            Irep_DB::table('irep_projects')->where('id', '=', $project_id)
                ->update(['polygon_data' => $array]);
        }



        $blocks = Irep_DB::table("irep_blocks")->where('project_id', '=', $project_id)->get();
        $floors = Irep_DB::table("irep_floors")->where('project_id', '=', $project_id)->get();


        $blocks_floors = [$blocks, $floors];



        foreach ($blocks_floors as  $index => $array) {
            $table = $index === 0 ? 'irep_blocks' : 'irep_floors';

            if (isset($array)) {
                foreach ($array as $item) {
                    $item = (array) $item;

                    $id = $item['id'];
                    $array = $item['polygon_data'];

                    $array = irep_handle_json_data($array);

                    $array = $this->fix_polygon_data($array, $id_mapping);

                    $array = irep_handle_json_data($array);

                    Irep_DB::table($table)->where('id', '=', $id)
                        ->update(['polygon_data' => $array]);
                }
            }
        }


        // Fix project_views polygon_data references inside project
        if (!empty($project['project_views'])) {
            $views = irep_handle_json_data($project['project_views']);
            if (is_array($views)) {
                foreach ($views as &$view) {
                    if (!empty($view['polygon_data']) && is_array($view['polygon_data'])) {
                        $view['polygon_data'] = $this->fix_polygon_data($view['polygon_data'], $id_mapping);
                    }
                }
                unset($view);
                Irep_DB::table('irep_projects')->where('id', '=', $project_id)
                    ->update(['project_views' => wp_json_encode($views)]);
            }
        }

        // Fix 360images polygon_data references inside project
        if ($addon_360_active && !empty($project['360images'])) {
            $images_360 = irep_handle_json_data($project['360images']);
            if (is_array($images_360)) {
                foreach ($images_360 as &$img) {
                    if (!empty($img['polygon_data']) && is_array($img['polygon_data'])) {
                        $img['polygon_data'] = $this->fix_polygon_data($img['polygon_data'], $id_mapping);
                    }
                }
                unset($img);
                Irep_DB::table('irep_projects')->where('id', '=', $project_id)
                    ->update(['360images' => wp_json_encode($images_360)]);
            }
        }

        // Fix 360images polygon_data references inside each block
        foreach ($blocks as $block) {
            if (!$addon_360_active) continue;
            $block = (array) $block;
            if (empty($block['360images'])) continue;
            $images_360 = irep_handle_json_data($block['360images']);
            if (!is_array($images_360)) continue;
            foreach ($images_360 as &$img) {
                if (!empty($img['polygon_data']) && is_array($img['polygon_data'])) {
                    $img['polygon_data'] = $this->fix_polygon_data($img['polygon_data'], $id_mapping);
                }
            }
            unset($img);
            Irep_DB::table('irep_blocks')->where('id', '=', $block['id'])
                ->update(['360images' => wp_json_encode($images_360)]);
        }

        irep_send_json_response(true, 'success');
    }

    /**
     * Extract a project ZIP, remap image URLs, and return the decoded project data array.
     * Returns an error string on failure.
     *
     * @return array|string
     */
    private function extract_zip_data(string $zip_tmp)
    {
        $zip = new ZipArchive();
        if ($zip->open($zip_tmp) !== true) {
            return 'Failed to open ZIP file.';
        }

        $tmp_dir = sys_get_temp_dir() . '/irep_import_' . uniqid();
        wp_mkdir_p($tmp_dir);
        $zip->extractTo($tmp_dir);
        $zip->close();

        $project_json_path = $tmp_dir . '/project_data.json';
        $image_map_path    = $tmp_dir . '/image_map.json';

        if (!file_exists($project_json_path)) {
            $this->rrmdir($tmp_dir);
            return 'Invalid ZIP: missing project_data.json.';
        }

        $project_json = file_get_contents($project_json_path);
        $image_map    = file_exists($image_map_path)
            ? json_decode(file_get_contents($image_map_path), true)
            : [];

        $attachment_id_map_path = $tmp_dir . '/attachment_id_map.json';
        $old_attachment_id_map  = file_exists($attachment_id_map_path)
            ? json_decode(file_get_contents($attachment_id_map_path), true)
            : [];

        // Strip 360 images from image_map if the addon is not active on this site.
        if (!irep_is_building_360_addon_active()) {
            $urls_360_path = $tmp_dir . '/360_urls.json';
            $urls_360      = file_exists($urls_360_path)
                ? json_decode(file_get_contents($urls_360_path), true)
                : [];
            if (!empty($urls_360) && is_array($urls_360)) {
                $urls_360_set = array_flip($urls_360);
                $image_map    = array_filter($image_map, fn($url) => !isset($urls_360_set[$url]), ARRAY_FILTER_USE_KEY);
            }
        }

        $url_to_new_id = [];
        $url_remap     = [];

        if (!empty($image_map) && is_array($image_map)) {
            $upload_dir = wp_upload_dir();

            require_once ABSPATH . 'wp-admin/includes/image.php';
            require_once ABSPATH . 'wp-admin/includes/file.php';
            require_once ABSPATH . 'wp-admin/includes/media.php';

            foreach ($image_map as $original_url => $zip_entry) {
                $src = $tmp_dir . '/' . $zip_entry;
                if (!file_exists($src)) {
                    continue;
                }

                $basename      = basename($zip_entry);
                $existing_path = $upload_dir['path'] . '/' . $basename;

                // Reuse existing file if one with the same name is already in the uploads dir.
                if (file_exists($existing_path)) {
                    $existing_url = $upload_dir['url'] . '/' . $basename;
                    $url_remap[$original_url] = $existing_url;

                    $existing_id = attachment_url_to_postid($existing_url);
                    if ($existing_id) {
                        $url_to_new_id[$existing_url] = $existing_id;
                    }
                    continue;
                }

                $dest    = $existing_path;
                $new_url = $upload_dir['url'] . '/' . $basename;

                // Always register the remap so 360images URL strings are fixed even when
                // the file copy fails (the image will 404 rather than hit a CORS error).
                $url_remap[$original_url] = $new_url;

                if (!copy($src, $dest)) {
                    continue;
                }

                $mime_type  = mime_content_type($dest) ?: 'image/jpeg';
                $attachment = [
                    'post_mime_type' => $mime_type,
                    'post_title'     => sanitize_file_name(pathinfo($basename, PATHINFO_FILENAME)),
                    'post_status'    => 'inherit',
                ];
                $new_id = wp_insert_attachment($attachment, $dest);
                if ($new_id && !is_wp_error($new_id)) {
                    $meta = wp_generate_attachment_metadata($new_id, $dest);
                    wp_update_attachment_metadata($new_id, $meta);
                    $url_to_new_id[$new_url] = $new_id;
                }
            }

        }

        $this->rrmdir($tmp_dir);

        $decoded = json_decode($project_json, true);
        if (!is_array($decoded)) {
            return 'Invalid project_data.json in ZIP.';
        }

        // Replace localhost URLs in the decoded data (360images etc. store URLs directly as strings).
        // Must be done on the decoded PHP array — str_replace on the raw JSON fails because
        // json_encode escapes forward slashes (/ → \/) so plain URLs never match.
        if (!empty($url_remap)) {
            $decoded = $this->remap_urls_in_data($decoded, $url_remap);
        }

        // Fallback: any 360images `img` URLs still pointing to the source site (i.e. not in the
        // image_map because the file was missing from the ZIP) get their domain replaced with the
        // current site's URL so they produce a 404 rather than a CORS error.
        $source_domains = [];
        foreach (array_keys($url_remap) as $old_url) {
            $parsed = parse_url($old_url);
            if (!$parsed) continue;
            $domain = ($parsed['scheme'] ?? 'http') . '://' . ($parsed['host'] ?? '');
            if (!empty($parsed['port'])) $domain .= ':' . $parsed['port'];
            $source_domains[$domain] = true;
        }
        if (!empty($source_domains)) {
            $decoded = $this->fix_360_image_domains($decoded, array_keys($source_domains));
        }

        // Remap attachment IDs (project_image, block_image, floor_image, image_2d, image_3d, gallery)
        if (!empty($old_attachment_id_map) && is_array($old_attachment_id_map) && !empty($url_to_new_id)) {
            $id_remap = [];
            foreach ($old_attachment_id_map as $old_id => $old_url) {
                $new_url = $url_remap[$old_url] ?? null;
                if ($new_url && isset($url_to_new_id[$new_url])) {
                    $id_remap[intval($old_id)] = $url_to_new_id[$new_url];
                }
            }
            if (!empty($id_remap)) {
                $decoded = $this->remap_attachment_ids($decoded, $id_remap);
            }
        }

        return $decoded;
    }

    /**
     * Collect all image URLs from all export data tables.
     */
    private function collect_image_data(array $export_data): array
    {
        $urls      = [];
        $id_map    = [];
        $urls_360  = [];

        $project = (array) $export_data['irep_project'];
        $this->resolve_attachment_id_url($project['project_image'] ?? 0, $urls, $id_map);
        $this->extract_urls_from_field($project, '360images', $urls_360);
        $this->extract_urls_from_field($project, 'project_views', $urls);

        foreach ($export_data['irep_blocks'] as $block) {
            $block = (array) $block;
            $this->resolve_attachment_id_url($block['block_image'] ?? 0, $urls, $id_map);
            $this->extract_urls_from_field($block, '360images', $urls_360);
        }

        foreach ($export_data['irep_floors'] as $floor) {
            $floor = (array) $floor;
            $this->resolve_attachment_id_url($floor['floor_image'] ?? 0, $urls, $id_map);
        }

        foreach ($export_data['irep_types'] as $type) {
            $type = (array) $type;
            $this->resolve_attachment_ids_json($type['image_2d'] ?? null, $urls, $id_map);
            $this->resolve_attachment_ids_json($type['image_3d'] ?? null, $urls, $id_map);
            $this->resolve_attachment_ids_json($type['gallery'] ?? null, $urls, $id_map);
        }

        $all_urls = array_unique(array_filter(array_merge($urls, $urls_360)));

        return [
            'urls'     => $all_urls,
            'id_map'   => $id_map,
            'urls_360' => array_unique(array_filter($urls_360)),
        ];
    }

    private function resolve_attachment_id_url($id, array &$urls, array &$id_map): void
    {
        $id = intval($id);
        if ($id <= 0) return;
        $url = wp_get_attachment_url($id);
        if ($url) {
            $urls[]       = $url;
            $id_map[$id]  = $url;
        }
    }

    private function resolve_attachment_ids_json($value, array &$urls, array &$id_map): void
    {
        if (empty($value)) return;
        $ids = is_array($value) ? $value : json_decode($value, true);
        if (!is_array($ids)) return;
        foreach ($ids as $id) {
            $this->resolve_attachment_id_url($id, $urls, $id_map);
        }
    }

    /**
     * Pull URL strings from a single field value into $urls.
     * Handles plain string URL, JSON array of strings, and JSON array of objects with a 'url' key.
     */
    private function extract_urls_from_field(array $row, string $field, array &$urls): void
    {
        if (empty($row[$field])) {
            return;
        }

        $value = $row[$field];

        if (is_string($value)) {
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                $value = $decoded;
            }
        }

        if (is_string($value)) {
            if (filter_var($value, FILTER_VALIDATE_URL)) {
                $urls[] = $value;
            }
            return;
        }

        if (is_array($value)) {
            foreach ($value as $item) {
                if (is_string($item) && filter_var($item, FILTER_VALIDATE_URL)) {
                    $urls[] = $item;
                } elseif (is_array($item) && !empty($item['url']) && filter_var($item['url'], FILTER_VALIDATE_URL)) {
                    $urls[] = $item['url'];
                } elseif (is_array($item) && !empty($item['img']) && filter_var($item['img'], FILTER_VALIDATE_URL)) {
                    $urls[] = $item['img'];
                }
            }
        }
    }

    private function remap_attachment_ids(array $decoded, array $id_remap): array
    {
        $remap_single = function ($id) use ($id_remap) {
            $id = intval($id);
            return $id_remap[$id] ?? $id;
        };

        $remap_ids_json = function ($value) use ($id_remap) {
            $ids = is_array($value) ? $value : json_decode($value, true);
            if (!is_array($ids)) return $value;
            $remapped = array_map(function ($id) use ($id_remap) {
                $id = intval($id);
                return $id_remap[$id] ?? $id;
            }, $ids);
            return is_array($value) ? $remapped : wp_json_encode($remapped);
        };

        if (isset($decoded['irep_project']['project_image'])) {
            $decoded['irep_project']['project_image'] = $remap_single($decoded['irep_project']['project_image']);
        }

        foreach ($decoded['irep_blocks'] as &$block) {
            if (isset($block['block_image'])) {
                $block['block_image'] = $remap_single($block['block_image']);
            }
        }
        unset($block);

        foreach ($decoded['irep_floors'] as &$floor) {
            if (isset($floor['floor_image'])) {
                $floor['floor_image'] = $remap_single($floor['floor_image']);
            }
        }
        unset($floor);

        foreach ($decoded['irep_types'] as &$type) {
            foreach (['image_2d', 'image_3d', 'gallery'] as $field) {
                if (array_key_exists($field, $type)) {
                    $type[$field] = $remap_ids_json($type[$field]);
                }
            }
        }
        unset($type);

        return $decoded;
    }

    /**
     * Replace the domain portion of any `img` URL in 360images fields that still belongs to
     * one of the source site domains. Used as a fallback when the image file wasn't in the
     * ZIP so no url_remap entry exists for it.
     */
    private function fix_360_image_domains(array $decoded, array $source_domains): array
    {
        $current_origin = rtrim(home_url('/'), '/');

        $fix_field = function ($value) use ($source_domains, $current_origin) {
            $images = is_array($value) ? $value : json_decode((string) $value, true);
            if (!is_array($images)) return $value;

            $changed = false;
            foreach ($images as &$img) {
                if (!is_array($img) || empty($img['img'])) continue;
                $parsed = parse_url($img['img']);
                if (!$parsed || empty($parsed['host'])) continue;
                $img_domain = ($parsed['scheme'] ?? 'http') . '://' . $parsed['host'];
                if (!empty($parsed['port'])) $img_domain .= ':' . $parsed['port'];
                if (in_array($img_domain, $source_domains, true) && $img_domain !== $current_origin) {
                    $img['img'] = $current_origin . ($parsed['path'] ?? '');
                    $changed    = true;
                }
            }
            unset($img);

            if (!$changed) return $value;
            return is_array($value) ? $images : wp_json_encode($images);
        };

        if (!empty($decoded['irep_project']['360images'])) {
            $decoded['irep_project']['360images'] = $fix_field($decoded['irep_project']['360images']);
        }

        if (!empty($decoded['irep_blocks']) && is_array($decoded['irep_blocks'])) {
            foreach ($decoded['irep_blocks'] as &$block) {
                if (!empty($block['360images'])) {
                    $block['360images'] = $fix_field($block['360images']);
                }
            }
            unset($block);
        }

        return $decoded;
    }

    /**
     * Recursively walk decoded JSON data and replace old URLs with new ones.
     * Handles nested arrays and JSON-encoded strings (e.g. 360images stored as a JSON string).
     */
    private function remap_urls_in_data($value, array $url_remap)
    {
        if (is_string($value)) {
            return strtr($value, $url_remap);
        }

        if (is_array($value)) {
            foreach ($value as $k => $v) {
                $value[$k] = $this->remap_urls_in_data($v, $url_remap);
            }
        }

        return $value;
    }

    /**
     * Recursively remove a temp directory.
     */
    private function rrmdir(string $dir): void
    {
        if (!is_dir($dir)) {
            return;
        }
        foreach (scandir($dir) as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }
            $path = $dir . '/' . $item;
            if (is_dir($path)) {
                $this->rrmdir($path);
            } else {
                unlink($path);
            }
        }
        rmdir($dir);
    }

    private function prepare_polygon_svg_data($item, $array = [])
    {

        unset($item['id']);
        unset($item['created_at']);
        unset($item['updated_at']);

        foreach ($array as $key) {
            $item[$key] = irep_transformSvgString($item[$key]);
        }

        return $item;
    }

    /**
     * Ensure LONGTEXT JSON columns are valid JSON strings. Do not run irep_transformSvgString on JSON
     * (it strips backslashes and breaks \u escapes and image ID arrays).
     *
     * @param mixed $value Raw value from export (string, array, or null).
     *
     * @return mixed
     */
    private function normalize_stored_json_field($value)
    {
        if ($value === null || $value === '') {
            return $value;
        }

        if (is_array($value)) {
            return wp_json_encode($value);
        }

        if (!is_string($value)) {
            return $value;
        }

        $decoded = json_decode(wp_unslash($value), true);
        if (json_last_error() === JSON_ERROR_NONE && (is_array($decoded) || is_object($decoded))) {
            return wp_json_encode($decoded);
        }

        return $value;
    }

    private function filter_table_row(array $row, array $allowed_keys): array
    {
        return array_intersect_key($row, array_flip($allowed_keys));
    }

    private function fix_polygon_data($array, $id_mapping)
    {
        foreach ($array as &$item) {
            if ($item['type'] === 'floor') {
                $item['id'] = strval($id_mapping['irep_floors'][$item['id']]);
            } else if ($item['type'] === 'tooltip') {
                $item['id'] = strval($id_mapping['irep_tooltip'][$item['id']]);
            } else if ($item['type'] === 'flat') {
                $item['id'] = strval($id_mapping['irep_flats'][$item['id']]);
            } else if ($item['type'] === 'block') {
                $item['id'] = strval($id_mapping['irep_blocks'][$item['id']]);
            }
        }

        return $array;
    }

    private function can_import_or_export()
    {
        return ire_fs()->can_use_premium_code();
    }
}

// Initialize the Irep_Project class.
$irep_import_export = new Irep_Import_Export();

/**
 * AJAX handler function to retrieve all projects.
 */
function irep_export()
{
    global $irep_import_export;
    $irep_import_export->export_data($_POST);
}

function irep_export_zip()
{
    global $irep_import_export;
    $irep_import_export->export_zip($_POST);
}

function irep_import()
{
    global $irep_import_export;
    $irep_import_export->import_data($_POST);
}

add_action('wp_ajax_irep_export', 'irep_export');
add_action('wp_ajax_irep_export_zip', 'irep_export_zip');
add_action('wp_ajax_irep_import', 'irep_import');
