<?php
/**
 * LinkCentral Import Shared Utilities
 *
 * This file contains shared utilities and common functionality used across different import types.
 * These utilities can be reused by CSV, JSON, ThirstyAffiliates, and other import implementations.
 */

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class LinkCentral_Import_Shared {

    /**
     * Validate basic link data structure
     *
     * @param array $link_data The link data to validate
     * @return true|WP_Error True if valid, WP_Error if invalid
     */
    public static function validate_link_data($link_data) {
        if (empty($link_data['title'])) {
            return new WP_Error('validation_error', __('Title is required.', 'linkcentral'));
        }

        if (empty($link_data['destination_url'])) {
            return new WP_Error('validation_error', __('Destination URL is required.', 'linkcentral'));
        }

        // Use the sanitization function to validate - if it returns empty, the URL is invalid
        $sanitized_url = linkcentral_sanitize_destination_url($link_data['destination_url']);
        if (empty($sanitized_url)) {
            return new WP_Error('validation_error', __('Invalid destination URL format.', 'linkcentral'));
        }

        if (empty($link_data['slug'])) {
            return new WP_Error('validation_error', __('Slug is required.', 'linkcentral'));
        }

        return true;
    }

    /**
     * Process slug and determine prefix settings
     *
     * @param string $raw_slug The raw slug from import
     * @param string $current_prefix Current URL prefix setting
     * @param bool $force_prefix Whether to force prefix usage
     * @return array Array with processed slug and prefix settings
     */
    public static function process_slug($raw_slug, $current_prefix, $force_prefix = false) {
        if (empty($raw_slug)) {
            return array(
                'slug' => '',
                'disable_slug_prefix' => false
            );
        }

        // Remove leading/trailing slashes
        $slug = trim($raw_slug, '/');

        // If force_prefix is enabled, always ensure the slug uses the current prefix
        if ($force_prefix) {
            // Check if it already starts with the current prefix
            if (strpos($slug, $current_prefix . '/') === 0) {
                // Already has the correct prefix, just remove it from slug and enable prefix
                return array(
                    'slug' => substr($slug, strlen($current_prefix) + 1),
                    'disable_slug_prefix' => false
                );
            } else {
                // Doesn't have the current prefix, add it by keeping the full slug and enabling prefix
                return array(
                    'slug' => $slug,
                    'disable_slug_prefix' => false
                );
            }
        }

        // When force_prefix is disabled, preserve the original slug exactly as it is
        // Only strip the current prefix if it exists, otherwise keep everything
        if (strpos($slug, $current_prefix . '/') === 0) {
            // Has our current prefix - strip it and enable prefix
            return array(
                'slug' => substr($slug, strlen($current_prefix) + 1),
                'disable_slug_prefix' => false
            );
        }

        // For any other case (no prefix, different prefix, multi-level), preserve exactly and disable prefix
        return array(
            'slug' => $slug,
            'disable_slug_prefix' => true
        );
    }

    /**
     * Find existing link by slug
     *
     * @param string $slug The slug to search for
     * @return WP_Post|null The existing post or null if not found
     */
    public static function find_existing_link($slug) {
        global $wpdb;

        $post_id = $wpdb->get_var($wpdb->prepare(
            "SELECT ID FROM $wpdb->posts WHERE post_name = %s AND post_type = %s LIMIT 1",
            $slug,
            'linkcentral_link'
        ));

        return $post_id ? get_post($post_id) : null;
    }

    /**
     * Build a concise label for an imported item.
     *
     * @param array|object|string $item Item data or a plain label.
     * @param string       $fallback Fallback label when no title or slug is available.
     * @return string
     */
    public static function format_import_item_label($item, $fallback = '') {
        if (is_string($item)) {
            $label = trim($item);
            if ($label !== '') {
                return $label;
            }
        }

        if (is_object($item)) {
            $item = get_object_vars($item);
        }

        if (!is_array($item)) {
            return $fallback !== '' ? $fallback : __('Unknown link', 'linkcentral');
        }

        $title = '';
        foreach (array('title', 'name', 'post_title', 'link_title') as $key) {
            if (!empty($item[$key])) {
                $title = trim((string) $item[$key]);
                break;
            }
        }

        $slug = '';
        foreach (array('slug', 'post_name', 'link_name') as $key) {
            if (!empty($item[$key])) {
                $slug = trim((string) $item[$key], '/');
                break;
            }
        }

        if ($title !== '' && $slug !== '' && $title !== $slug) {
            return $title . ' (' . $slug . ')';
        }

        if ($title !== '') {
            return $title;
        }

        if ($slug !== '') {
            return $slug;
        }

        return $fallback !== '' ? $fallback : __('Unknown link', 'linkcentral');
    }

    /**
     * Add one skipped link to the import results.
     *
     * @param array        $results Import results array, passed by reference.
     * @param array|object|string $item Skipped item data or label.
     * @param array        $context Optional context such as row number.
     * @return void
     */
    public static function add_skipped_link(&$results, $item, $context = array()) {
        if (!isset($results['skipped_links']) || !is_array($results['skipped_links'])) {
            $results['skipped_links'] = array();
        }

        $skipped_link = array(
            'link' => self::format_import_item_label($item),
        );

        if (!empty($context['row'])) {
            $skipped_link['row'] = (int) $context['row'];
        }

        if (!empty($context['existing_link'])) {
            $skipped_link['existing_link'] = self::format_import_item_label($context['existing_link']);
        }

        $results['skipped_links'][] = $skipped_link;
    }

    /**
     * Create or update a LinkCentral link
     *
     * @param array $link_data The link data
     * @param WP_Post|null $existing_link Existing link if updating
     * @param array $import_settings Import configuration settings
     * @return int|WP_Error Post ID on success, WP_Error on failure
     */
    public static function create_or_update_link($link_data, $existing_link, $import_settings) {
        // Prepare post data
        $post_data = array(
            'post_type' => 'linkcentral_link',
            'post_title' => $link_data['title'],
            'post_name' => linkcentral_sanitize_input_slug($link_data['slug']),
            'post_status' => 'publish',
            'post_author' => get_current_user_id()
        );

        if ($existing_link && $import_settings['duplicate_handling'] === 'update') {
            $post_data['ID'] = $existing_link->ID;
            $post_id = wp_update_post($post_data);
        } else {
            $post_id = wp_insert_post($post_data);
        }

        if (is_wp_error($post_id) || !$post_id) {
            return new WP_Error('post_error', __('Failed to create/update post.', 'linkcentral'));
        }

        // Update meta fields
        update_post_meta($post_id, '_linkcentral_destination_url', linkcentral_sanitize_destination_url($link_data['destination_url']));
        update_post_meta($post_id, '_linkcentral_nofollow', $link_data['nofollow']);
        update_post_meta($post_id, '_linkcentral_sponsored', $link_data['sponsored']);
        update_post_meta($post_id, '_linkcentral_parameter_forwarding', $link_data['parameter_forwarding']);
        update_post_meta($post_id, '_linkcentral_redirection_type', $link_data['redirect_type']);
        update_post_meta($post_id, '_linkcentral_disable_slug_prefix', $link_data['disable_slug_prefix']);

        if (!empty($link_data['note'])) {
            update_post_meta($post_id, '_linkcentral_note', sanitize_textarea_field($link_data['note']));
        }

        // Handle category - always try to assign categories, but respect create_categories setting
        if (!empty($link_data['category'])) {
            self::assign_category($post_id, $link_data['category'], $import_settings['create_categories'] ?? true);
        }

        // Handle keywords for auto-keyword linking
        if (!empty($link_data['keywords'])) {
            self::import_keywords_from_string($post_id, $link_data['keywords']);
        }

        return $post_id;
    }

    /**
     * Assign category to link
     *
     * @param int $post_id The post ID
     * @param string|array $category_data Category data
     * @param bool $create_categories Whether to create categories if they don't exist
     */
    public static function assign_category($post_id, $category_data, $create_categories = true) {
        if (empty($category_data)) {
            return;
        }

        $term_ids = array();

        // Handle different input types
        if (is_string($category_data)) {
            // CSV import: categories come as string
            $category_string = trim($category_data);
            if (empty($category_string)) {
                return;
            }

            // Split by semicolons to support multiple categories
            $category_names = array_filter(array_map('trim', explode(';', $category_string)));

            foreach ($category_names as $category_name) {
                $term_id = self::get_or_create_category($category_name, $create_categories);
                if ($term_id) {
                    $term_ids[] = $term_id;
                }
            }
        } elseif (is_array($category_data)) {
            // JSON import: categories come as hierarchical paths like "Parent > Child"
            foreach ($category_data as $category_path) {
                $term_id = self::get_or_create_category($category_path, $create_categories);
                if ($term_id) {
                    $term_ids[] = $term_id;
                }
            }
        }

        // Assign all found categories to post
        if (!empty($term_ids)) {
            wp_set_post_terms($post_id, $term_ids, 'linkcentral_category');
        }
    }

    /**
     * Get or create category from hierarchical path like "Parent > Child > Grandchild"
     *
     * @param string $category_path The category path
     * @param bool $create_categories Whether to create if doesn't exist
     * @return int|null The term ID or null on failure
     */
    public static function get_or_create_category($category_path, $create_categories = true) {
        if (empty($category_path)) {
            return null;
        }

        // Split path into parts
        $path_parts = array_map('trim', explode(' > ', $category_path));
        $parent_id = 0;
        $term_id = null;

        // Walk through each level of the hierarchy
        foreach ($path_parts as $category_name) {
            if (empty($category_name)) {
                continue;
            }

            // Look for existing term at this level with this parent
            $existing_term = get_terms(array(
                'taxonomy' => 'linkcentral_category',
                'name' => $category_name,
                'parent' => $parent_id,
                'hide_empty' => false,
                'number' => 1
            ));

            if (!empty($existing_term)) {
                $term_id = $existing_term[0]->term_id;
                $parent_id = $term_id; // This becomes the parent for the next level
            } elseif ($create_categories) {
                // Create new category at this level
                $args = array();
                if ($parent_id) {
                    $args['parent'] = $parent_id;
                }

                $term_result = wp_insert_term($category_name, 'linkcentral_category', $args);
                if (!is_wp_error($term_result)) {
                    $term_id = $term_result['term_id'];
                    $parent_id = $term_id; // This becomes the parent for the next level
                } else {
                    return null; // Failed to create category
                }
            } else {
                return null; // Category doesn't exist and we're not creating
            }
        }

        return $term_id; // Return the final (deepest) category ID
    }

    /**
     * Import keywords from string format
     *
     * @param int $post_id The post ID
     * @param string $keywords_string The keywords string
     */
    public static function import_keywords_from_string($post_id, $keywords_string) {
        if (empty($keywords_string)) {
            return;
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'linkcentral_keywords';

        // Delete existing keywords for this link
        $wpdb->delete($table_name, array('link_id' => $post_id), array('%d'));

        // Parse keywords string - support multiple formats:
        // 1. Comma-separated: "keyword1,keyword2,keyword3"
        // 2. Semicolon-separated: "keyword1;keyword2;keyword3"
        // 3. Pipe-separated: "keyword1|keyword2|keyword3"
        // 4. With density: "keyword1:high,keyword2:medium,keyword3:low"

        $keywords = array();

        // First, try to split by common delimiters
        if (strpos($keywords_string, ',') !== false) {
            $keyword_parts = explode(',', $keywords_string);
        } elseif (strpos($keywords_string, ';') !== false) {
            $keyword_parts = explode(';', $keywords_string);
        } elseif (strpos($keywords_string, '|') !== false) {
            $keyword_parts = explode('|', $keywords_string);
        } else {
            $keyword_parts = array($keywords_string);
        }

        foreach ($keyword_parts as $keyword_part) {
            $keyword_part = trim($keyword_part);
            if (empty($keyword_part)) {
                continue;
            }

            // Check if density is specified (format: keyword:density)
            if (strpos($keyword_part, ':') !== false) {
                $parts = explode(':', $keyword_part, 2);
                $keyword = trim($parts[0]);
                $density = trim($parts[1]);

                // Validate density
                if (!in_array($density, array('low', 'medium', 'high'))) {
                    $density = 'medium'; // Default to medium if invalid
                }
            } else {
                $keyword = $keyword_part;
                $density = 'medium'; // Default density
            }

            if (!empty($keyword)) {
                $keywords[] = array(
                    'keyword' => $keyword,
                    'density' => $density
                );
            }
        }

        // Insert keywords into database
        foreach ($keywords as $keyword_data) {
            $data = array(
                'link_id' => $post_id,
                'keyword' => sanitize_text_field($keyword_data['keyword']),
                'density' => $keyword_data['density']
            );

            $wpdb->insert($table_name, $data, array('%d', '%s', '%s'));
        }
    }

    /**
     * Count keywords in a string
     *
     * @param string $keywords_string The keywords string
     * @return int Number of keywords
     */
    public static function count_keywords_in_string($keywords_string) {
        if (empty($keywords_string)) {
            return 0;
        }

        // Parse keywords string using the same logic as import_keywords_from_string
        if (strpos($keywords_string, ',') !== false) {
            $keyword_parts = explode(',', $keywords_string);
        } elseif (strpos($keywords_string, ';') !== false) {
            $keyword_parts = explode(';', $keywords_string);
        } elseif (strpos($keywords_string, '|') !== false) {
            $keyword_parts = explode('|', $keywords_string);
        } else {
            $keyword_parts = array($keywords_string);
        }

        $count = 0;
        foreach ($keyword_parts as $keyword_part) {
            $keyword_part = trim($keyword_part);
            if (empty($keyword_part)) {
                continue;
            }

            // Check if density is specified (format: keyword:density)
            if (strpos($keyword_part, ':') !== false) {
                $parts = explode(':', $keyword_part, 2);
                $keyword = trim($parts[0]);
            } else {
                $keyword = $keyword_part;
            }

            if (!empty($keyword)) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * Parse boolean values from various formats
     *
     * @param string $value The value to parse
     * @return string The normalized boolean value
     */
    public static function parse_boolean_value($value) {
        $value = strtolower(trim($value));

        // Explicitly handle "enabled" values
        if (in_array($value, array('1', 'true', 'yes', 'on', 'enabled'))) {
            return 'yes';
        }

        // Explicitly handle "disabled" values
        if (in_array($value, array('0', 'false', 'no', 'off', 'disabled'))) {
            return 'no';
        }

        // Handle global/default values - normalize "global" to "default"
        return self::normalize_global_value($value);
    }

    /**
     * Parse redirect type and ensure it's valid
     *
     * @param string $value The redirect type value
     * @return string The normalized redirect type
     */
    public static function parse_redirect_type($value) {
        $value = trim($value);
        $valid_types = array('301', '302', '307');

        if (in_array($value, $valid_types)) {
            return $value;
        }

        // Try to map common variations
        if (in_array(strtolower($value), array('permanent', '301'))) {
            return '301';
        } elseif (in_array(strtolower($value), array('temporary', '302'))) {
            return '302';
        }

        // Handle global/default values - normalize "global" to "default"
        return self::normalize_global_value($value);
    }

    /**
     * Normalize "global" values to "default" for consistency
     *
     * @param string $value The value to normalize
     * @return string The normalized value
     */
    public static function normalize_global_value($value) {
        $value = trim($value);
        if (strtolower($value) === 'global' || $value === '') {
            return 'default';
        }
        return $value;
    }

    /**
     * Import keywords for a link (from array format)
     *
     * @param int $link_id The link ID
     * @param array $keywords_data The keywords data
     */
    public static function import_keywords_for_link($link_id, $keywords_data) {
        if (empty($keywords_data)) {
            return;
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'linkcentral_keywords';

        // Delete existing keywords for this link
        $wpdb->delete($table_name, array('link_id' => $link_id), array('%d'));

        // Import new keywords
        foreach ($keywords_data as $keyword) {
            if (!empty($keyword['keyword'])) {
                $data = array(
                    'link_id' => $link_id,
                    'keyword' => sanitize_text_field($keyword['keyword']),
                    'density' => in_array($keyword['density'], array('low', 'medium', 'high')) ? $keyword['density'] : 'medium'
                );

                $wpdb->insert($table_name, $data, array('%d', '%s', '%s'));
            }
        }
    }

    /**
     * Import analytics data for a link
     *
     * @param int $link_id The link ID
     * @param array $analytics_data The analytics data
     * @return int Number of analytics records imported
     */
    public static function import_analytics_data($link_id, $analytics_data) {
        if (empty($analytics_data)) {
            return 0;
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'linkcentral_stats';
        $imported_count = 0;

        foreach ($analytics_data as $click) {
            $data = array(
                'link_id'         => $link_id,
                'click_date'      => $click['click_date'],
                'referring_url'   => $click['referring_url'] ?? '',
                'user_agent'      => $click['user_agent'] ?? '',
                'visitor_id'      => $click['visitor_id'] ?? '',
                'destination_url' => $click['destination_url'] ?? '',
                'country'         => $click['country'] ?? '',
                'region'          => $click['region'] ?? '',
                'city'            => $click['city'] ?? '',
            );

            $result = $wpdb->insert($table_name, $data);
            if ($result !== false) {
                $imported_count++;
            }
        }

        return $imported_count;
    }
}
