//! URL generation and conversion utilities
//!
//! This module handles URL generation, localization, and path conversion
//! between different languages and content types.

// RouteMetadataAccess is only used by tools crate during build time
use std::collections::HashMap;

use super::cache::cached_route_lookup;
use super::components::RouteConfigToml;
use super::config::load_routes_config;
use super::language::{extract_lang_and_path, get_content_type_base_path};
use super::tracker::{get_tracker, NavigationEvent, NavigationEventType};
use crate::i18n::language_config::get_language_registry;

/// Fast version of get_localized_route without console logging for performance
/// Convert content post URLs between languages (handles dynamic slugs)
/// Generic function that works with any content type component (BlogPost,
/// RecipePost, etc.)
pub fn convert_content_post_url(
    path: &str,
    current_lang: &str,
    target_language: &str,
) -> Option<String> {
    #[cfg(target_arch = "wasm32")]
    web_sys::console::log_1(
        &format!(
            "🔄 WASM convert_content_post_url: '{}' from '{}' to '{}'",
            path, current_lang, target_language
        )
        .into(),
    );

    #[cfg(not(target_arch = "wasm32"))]
    tracing::warn!(
        "🔄 convert_content_post_url: '{}' from '{}' to '{}'",
        path,
        current_lang,
        target_language
    );

    let config = load_routes_config();

    // Check if this is a content post URL pattern by looking for matching routes
    // Sort routes by specificity (more parameters = more specific) and then by
    // priority
    let mut sorted_routes = config.routes.clone();
    sorted_routes.sort_by(|a, b| {
        // First compare by parameter count (more specific patterns first)
        let a_param_count = a.path.matches('{').count();
        let b_param_count = b.path.matches('{').count();

        match b_param_count.cmp(&a_param_count) {
            // Higher param count first
            std::cmp::Ordering::Equal => {
                // If same parameter count, compare by priority (higher priority first)
                b.get_priority()
                    .partial_cmp(&a.get_priority())
                    .unwrap_or(std::cmp::Ordering::Equal)
            }
            other => other,
        }
    });

    #[cfg(target_arch = "wasm32")]
    web_sys::console::log_1(
        &format!(
            "🔍 ROUTE MATCHING: Checking {} routes for path '{}'",
            sorted_routes.len(),
            path
        )
        .into(),
    );

    for current_route in &sorted_routes {
        if !current_route.get_enabled()
            || current_route.get_language() != Some(current_lang.to_string())
        {
            continue;
        }

        // Check if this route matches a content pattern with category and/or slug
        // Works with BlogPost, RecipePost, or any other content type with parametric
        // routes Handles both post URLs ({category}/{slug}) and category URLs
        // ({category} only)
        if current_route.path.contains("{category}") || current_route.path.contains("{slug}") {
            #[cfg(target_arch = "wasm32")]
            web_sys::console::log_1(
                &format!(
                    "🔍 TRYING PATTERN: '{}' with {} parameters",
                    current_route.path,
                    current_route.path.matches('{').count()
                )
                .into(),
            );

            // Try to match the path against this route pattern
            if let Some((category, current_slug)) =
                extract_content_params(&current_route.path, path)
            {
                #[cfg(target_arch = "wasm32")]
                web_sys::console::log_1(
                    &format!(
                        "🔍 EXTRACT PARAMS: pattern='{}', url='{}' => category='{}', slug='{}'",
                        current_route.path, path, category, current_slug
                    )
                    .into(),
                );
                // Find corresponding route in target language with the same component AND
                // content_type First, get the current route's content_type
                let current_content_type = current_route.content_type.clone();

                if let Some(target_route) = config.routes.iter().find(|r| {
                    r.get_enabled()
                        && r.get_language() == Some(target_language.to_string())
                        && r.component == current_route.component // Same component type
                        // Match route pattern: both routes must have the same parameter structure
                        && ((current_route.path.contains("{category}") && r.path.contains("{category}")) ||
                            (current_route.path.contains("{slug}") && r.path.contains("{slug}")))
                        // CRITICAL: Prioritize content_type matching when available
                        && (current_content_type.is_none() ||
                            r.content_type == current_content_type)
                }) {
                    // Get content type from route configuration or derive from component
                    let content_type = current_route.content_type.clone()
                        .or_else(|| {
                            get_content_type_from_component(&current_route.component)
                        })
                        .unwrap_or_else(|| {
                            // Fallback: extract from route path or use component name
                            if current_route.component.to_lowercase().contains("blog") {
                                "blog".to_string()
                            } else if current_route.component.to_lowercase().contains("recipe") {
                                "recipes".to_string()
                            } else {
                                "content".to_string() // Generic fallback
                            }
                        });

                    #[cfg(target_arch = "wasm32")]
                    web_sys::console::log_1(
                        &format!(
                            "🔍 CONTENT TRANSLATION: Looking for content ID with slug='{}', content_type='{}', language='{}'",
                            current_slug, content_type, current_lang
                        ).into(),
                    );

                    // Use the new content translation system to find the correct slug
                    // First, get the content ID from the current slug
                    if let Some(content_id) =
                        crate::content::item::UnifiedContentItem::get_content_id_from_slug(
                            &current_slug,
                            &content_type,
                            current_lang,
                        )
                    {
                        // Find the translated slug and category using the content ID
                        if let Some((translated_slug, translated_category_opt)) =
                            crate::content::item::UnifiedContentItem::find_translated_content_by_id(
                                &content_id,
                                &content_type,
                                target_language,
                            )
                        {
                            // Build the target URL with the correct translated slug and category
                            let mut target_url = target_route.path.clone();

                            // Replace {slug} with translated slug
                            target_url = target_url.replace("{slug}", &translated_slug);

                            // Replace {category} with translated category (or fallback to original)
                            if target_url.contains("{category}") {
                                let category_to_use = translated_category_opt.as_deref().unwrap_or(&category);
                                target_url = target_url.replace("{category}", category_to_use);
                            }

                            // Remove .html extension from slug if present and add it to the final URL
                            let final_url =
                                if path.ends_with(".html") && !target_url.ends_with(".html") {
                                    format!("{}.html", target_url)
                                } else {
                                    target_url
                                };

                            #[cfg(target_arch = "wasm32")]
                            web_sys::console::log_1(
                                &format!(
                                    "✅ CONTENT TRANSLATION: {} -> {} | ID: {} | Slug: {} -> {} | URL: {} -> {}",
                                    current_lang, target_language, content_id, current_slug, translated_slug, path, final_url
                                ).into()
                            );

                            return Some(final_url);
                        } else {
                            #[cfg(target_arch = "wasm32")]
                            web_sys::console::log_1(
                                &format!(
                                    "⚠️ CONTENT TRANSLATION: No translation found for ID '{}' in language '{}'",
                                    content_id, target_language
                                ).into()
                            );
                        }
                    } else {
                        #[cfg(target_arch = "wasm32")]
                        web_sys::console::log_1(
                            &format!(
                                "⚠️ CONTENT TRANSLATION: No content ID found for slug '{}' in language '{}' content_type '{}'",
                                current_slug, current_lang, content_type
                            ).into()
                        );

                        // Special handling for category-only pages: translate category name if possible
                        if current_slug == category && current_route.path.contains("{category}") && !current_route.path.contains("{slug}") {
                            #[cfg(target_arch = "wasm32")]
                            web_sys::console::log_1(
                                &format!("🏷️ CATEGORY TRANSLATION: Attempting to translate category '{}' from '{}' to '{}'",
                                    category, current_lang, target_language).into()
                            );

                            // Try to find a translated category by looking for any content in the target language
                            // that has posts with the same category in the source language
                            if let Some(translated_category) = find_translated_category_name(&category, &content_type, current_lang, target_language) {
                                let target_url = target_route.path.replace("{category}", &translated_category);

                                #[cfg(target_arch = "wasm32")]
                                web_sys::console::log_1(
                                    &format!(
                                        "✅ CATEGORY TRANSLATION: {} -> {} | Category: {} -> {} | URL: {} -> {}",
                                        current_lang, target_language, category, translated_category, path, target_url
                                    ).into()
                                );

                                return Some(target_url);
                            } else {
                                #[cfg(target_arch = "wasm32")]
                                web_sys::console::log_1(
                                    &format!("⚠️ CATEGORY TRANSLATION: No translation found for category '{}' in language '{}'",
                                        category, target_language).into()
                                );
                            }
                        }

                        // Fallback to old behavior for backward compatibility
                        let target_url = target_route
                            .path
                            .replace("{category}", &category)
                            .replace("{slug}", &current_slug);

                        let final_url = if path.ends_with(".html") && !target_url.ends_with(".html")
                        {
                            format!("{}.html", target_url)
                        } else {
                            target_url
                        };

                        return Some(final_url);
                    }
                }
            }
        }
    }

    None
}

/// Legacy function for backward compatibility
pub fn convert_blog_post_url(
    path: &str,
    current_lang: &str,
    target_language: &str,
) -> Option<String> {
    convert_content_post_url(path, current_lang, target_language)
}

/// Find the translated category name by looking up categories from content
/// index This searches the target language content index for posts that match
/// the source category
fn find_translated_category_name(
    source_category: &str,
    content_type: &str,
    source_lang: &str,
    target_lang: &str,
) -> Option<String> {
    // Load the source language content to find content IDs with this category
    let source_items =
        crate::content::item::UnifiedContentItem::load_for_type(content_type, source_lang)?;

    // Find any content item that has the source category
    let source_item_with_category = source_items
        .iter()
        .find(|item| item.categories.contains(&source_category.to_string()))?;

    // Get the content ID for this item
    let content_id = &source_item_with_category.id;

    // Load the target language content and find the same content ID
    let target_items =
        crate::content::item::UnifiedContentItem::load_for_type(content_type, target_lang)?;

    // Find the content item with the same ID in the target language
    let target_item = target_items.iter().find(|item| &item.id == content_id)?;

    // Return the translated category (first category from the categories vec)
    target_item.categories.first().cloned()
}

/// Extract category and slug parameters from a content URL
/// Generic function that works with any content type (blog, recipes, etc.)
/// Supports various patterns: {category}, {slug}, {category}/{slug},
/// post/{slug}, etc.
fn extract_content_params(pattern: &str, url: &str) -> Option<(String, String)> {
    // Remove .html extension for pattern matching
    let clean_url = url.strip_suffix(".html").unwrap_or(url);

    let category_start = pattern.find("{category}");
    let slug_start = pattern.find("{slug}");

    match (category_start, slug_start) {
        // Pattern has both {category} and {slug}
        (Some(cat_start), Some(slug_start)) => {
            extract_category_and_slug_params(pattern, clean_url, cat_start, slug_start)
        }
        // Pattern has only {category}
        (Some(cat_start), None) => extract_category_only_params(pattern, clean_url, cat_start),
        // Pattern has only {slug}
        (None, Some(slug_start)) => extract_slug_only_params(pattern, clean_url, slug_start),
        // Pattern has neither - shouldn't happen
        (None, None) => None,
    }
}

/// Extract both category and slug from patterns like
/// "/recetas/{category}/{slug}"
fn extract_category_and_slug_params(
    pattern: &str,
    url: &str,
    category_start: usize,
    slug_start: usize,
) -> Option<(String, String)> {
    // Extract the static parts of the pattern
    let prefix = &pattern[..category_start];
    let middle = &pattern[category_start + 10..slug_start]; // 10 = len("{category}")
    let suffix = &pattern[slug_start + 6..]; // 6 = len("{slug}")

    // Check if URL matches the pattern structure
    if !url.starts_with(prefix) {
        return None;
    }

    let after_prefix = &url[prefix.len()..];
    let middle_pos = after_prefix.find(middle)?;

    // Extract category
    let category = after_prefix[..middle_pos].to_string();

    // Extract slug
    let after_middle = &after_prefix[middle_pos + middle.len()..];
    let slug = if suffix.is_empty() {
        after_middle.to_string()
    } else {
        after_middle.strip_suffix(suffix)?.to_string()
    };

    if category.is_empty() || slug.is_empty() {
        return None;
    }

    Some((category, slug))
}

/// Extract only category from patterns like "/recetas/{category}"
fn extract_category_only_params(
    pattern: &str,
    url: &str,
    category_start: usize,
) -> Option<(String, String)> {
    let prefix = &pattern[..category_start];
    let suffix = &pattern[category_start + 10..]; // 10 = len("{category}")

    if !url.starts_with(prefix) {
        return None;
    }

    let after_prefix = &url[prefix.len()..];
    let category = if suffix.is_empty() {
        after_prefix.to_string()
    } else {
        after_prefix.strip_suffix(suffix)?.to_string()
    };

    if category.is_empty() {
        return None;
    }

    // For category-only patterns, return category as both category and slug
    // This allows the rest of the logic to work with the existing structure
    Some((category.clone(), category))
}

/// Extract only slug from patterns like "/recetas/post/{slug}"
fn extract_slug_only_params(
    pattern: &str,
    url: &str,
    slug_start: usize,
) -> Option<(String, String)> {
    let prefix = &pattern[..slug_start];
    let suffix = &pattern[slug_start + 6..]; // 6 = len("{slug}")

    if !url.starts_with(prefix) {
        return None;
    }

    let after_prefix = &url[prefix.len()..];
    let slug = if suffix.is_empty() {
        after_prefix.to_string()
    } else {
        after_prefix.strip_suffix(suffix)?.to_string()
    };

    if slug.is_empty() {
        return None;
    }

    // For slug-only patterns, return slug as both category and slug
    // This allows the rest of the logic to work with the existing structure
    Some((slug.clone(), slug))
}

pub fn get_localized_route(path: &str, target_language: &str) -> String {
    #[cfg(target_arch = "wasm32")]
    web_sys::console::log_1(
        &format!(
            "🌍 GET_LOCALIZED_ROUTE ENTRY: path='{}', target_language='{}'",
            path, target_language
        )
        .into(),
    );

    let language_registry = get_language_registry();
    let _default_language = language_registry.get_default_language();
    let (current_lang, _clean_path) = extract_lang_and_path(path);

    // Use cached route lookup to improve performance
    cached_route_lookup(path, &current_lang, target_language, || {
        get_localized_route_impl(path, target_language)
    })
}

/// Internal implementation of get_localized_route (for caching)
fn get_localized_route_impl(path: &str, target_language: &str) -> String {
    #[cfg(not(target_arch = "wasm32"))]
    use std::time::{SystemTime, UNIX_EPOCH};

    #[cfg(not(target_arch = "wasm32"))]
    let start_time = SystemTime::now();
    let language_registry = get_language_registry();
    let default_language = language_registry.get_default_language();
    let (current_lang, clean_path) = extract_lang_and_path(path);

    #[cfg(not(target_arch = "wasm32"))]
    tracing::warn!(
        "🔄 get_localized_route: Converting '{}' from '{}' to '{}' (clean_path: '{}')",
        path,
        current_lang,
        target_language,
        clean_path
    );

    #[cfg(target_arch = "wasm32")]
    web_sys::console::log_1(
        &format!(
            "🔄 WASM get_localized_route: Converting '{}' from '{}' to '{}' (clean_path: '{}')",
            path, current_lang, target_language, clean_path
        )
        .into(),
    );

    // If already in target language, return as-is
    if current_lang == target_language {
        #[cfg(not(target_arch = "wasm32"))]
        tracing::warn!(
            "✅ get_localized_route: Already in target language, returning '{}'",
            path
        );

        // Track this early return (only in non-WASM builds)
        #[cfg(not(target_arch = "wasm32"))]
        if let Some(tracker) = get_tracker() {
            if let Ok(mut tracker) = tracker.lock() {
                let elapsed = start_time.elapsed().unwrap_or_default();
                let mut additional_data = std::collections::HashMap::new();
                additional_data.insert("early_return".to_string(), "same_language".to_string());

                let event = NavigationEvent {
                    session_id: String::new(),
                    timestamp: start_time
                        .duration_since(UNIX_EPOCH)
                        .unwrap_or_default()
                        .as_millis() as u64,
                    event_type: NavigationEventType::RouteResolution,
                    source_path: path.to_string(),
                    target_path: Some(path.to_string()),
                    current_lang: current_lang.clone(),
                    target_lang: Some(target_language.to_string()),
                    resolution_time_ms: elapsed.as_millis() as u64,
                    cache_hit: true, // Same path is like a cache hit
                    component: None,
                    errors: Vec::new(),
                    fallback_used: false,
                    additional_data,
                };
                tracker.track_event(event);
            }
        }

        return path.to_string();
    }

    // Handle content post URLs with slugs first (before static route mappings)
    if let Some(converted_content_url) =
        convert_content_post_url(path, &current_lang, target_language)
    {
        #[cfg(target_arch = "wasm32")]
        web_sys::console::log_1(
            &format!(
                "✅ ROUTING: Content post URL converted: {} -> {}",
                path, converted_content_url
            )
            .into(),
        );

        // Track content post URL conversion (only in non-WASM builds)
        #[cfg(not(target_arch = "wasm32"))]
        if let Some(tracker) = get_tracker() {
            if let Ok(mut tracker) = tracker.lock() {
                let elapsed = start_time.elapsed().unwrap_or_default();
                let mut additional_data = std::collections::HashMap::new();
                additional_data.insert(
                    "conversion_type".to_string(),
                    "content_post_url".to_string(),
                );

                let event = NavigationEvent {
                    session_id: String::new(),
                    timestamp: start_time
                        .duration_since(UNIX_EPOCH)
                        .unwrap_or_default()
                        .as_millis() as u64,
                    event_type: NavigationEventType::RouteResolution,
                    source_path: path.to_string(),
                    target_path: Some(converted_content_url.clone()),
                    current_lang: current_lang.clone(),
                    target_lang: Some(target_language.to_string()),
                    resolution_time_ms: elapsed.as_millis() as u64,
                    cache_hit: false,
                    component: Some("content_post".to_string()),
                    errors: Vec::new(),
                    fallback_used: false,
                    additional_data,
                };
                tracker.track_event(event);
            }
        }

        return converted_content_url;
    }

    // Direct component-based lookup from configuration (most reliable method)
    let config = load_routes_config();

    // First try to find exact route match in current language
    let current_route = config.routes.iter().find(|r| {
        r.get_enabled() && r.get_language() == Some(current_lang.clone()) && r.path == path
    });

    if let Some(current_route) = current_route {
        #[cfg(not(target_arch = "wasm32"))]
        tracing::warn!(
            "🔍 get_localized_route: Found current route - path: '{}', component: '{:?}', \
             content_type: {:?}",
            current_route.path,
            current_route.component,
            current_route.content_type
        );

        // Look for corresponding route in target language with same component AND
        // content_type if available
        if let Some(target_route) = config.routes.iter().find(|r| {
            r.get_enabled()
                && r.get_language() == Some(target_language.to_string())
                && r.component == current_route.component
                && r.content_type == current_route.content_type
        }) {
            #[cfg(not(target_arch = "wasm32"))]
            tracing::warn!(
                "✅ get_localized_route: Found target route - path: '{}', component: '{:?}', \
                 content_type: {:?}",
                target_route.path,
                target_route.component,
                target_route.content_type
            );
            return target_route.path.clone();
        } else {
            #[cfg(not(target_arch = "wasm32"))]
            tracing::warn!(
                "⚠️ get_localized_route: No target route found for component '{:?}', \
                 content_type: {:?} in language '{}'",
                current_route.component,
                current_route.content_type,
                target_language
            );
        }
    } else {
        #[cfg(not(target_arch = "wasm32"))]
        tracing::warn!(
            "⚠️ get_localized_route: No current route found for path '{}' in language '{}'",
            path,
            current_lang
        );
    }

    // Fallback to original implementation for complex cases
    get_localized_route_fallback(
        path,
        target_language,
        &current_lang,
        &clean_path,
        default_language,
    )
}

/// Fallback implementation for complex route resolution
fn get_localized_route_fallback(
    path: &str,
    _target_language: &str,
    _current_lang: &str,
    _clean_path: &str,
    _default_language: &str,
) -> String {
    let _config = load_routes_config();

    // Return original path unchanged if no other method works
    #[cfg(not(target_arch = "wasm32"))]
    tracing::warn!(
        "⚠️ get_localized_route: No route mapping found for {} -> {} language, keeping original",
        path,
        _target_language
    );

    path.to_string()
}

/// Helper function to get content type from component name
pub fn get_content_type_from_component(component: &str) -> Option<String> {
    // Simple fallback logic based on component name patterns
    let component_lower = component.to_lowercase();
    if component_lower.contains("blog") {
        Some("blog".to_string())
    } else if component_lower.contains("recipe") {
        Some("recipes".to_string())
    } else {
        None
    }
}

/// Route information structure
#[derive(Debug, Clone)]
pub struct RouteInfo {
    pub path: String,
    pub language: String,
    pub clean_path: String,
    pub is_default_language: bool,
}

impl RouteInfo {
    /// Create RouteInfo from a path
    pub fn from_path(path: &str) -> Self {
        let language_registry = get_language_registry();
        let default_language = language_registry.get_default_language();
        let (language, clean_path) = extract_lang_and_path(path);
        let is_default_language = language == default_language;

        Self {
            path: path.to_string(),
            language,
            clean_path,
            is_default_language,
        }
    }

    /// Get alternate language version of this route
    pub fn get_alternate(&self, target_language: &str) -> String {
        get_localized_route(&self.path, target_language)
    }
}

/// Validate route format
pub fn is_valid_route_format(path: &str) -> bool {
    // Basic validation - starts with /, no double slashes, no query params for now
    path.starts_with('/') && !path.contains("//") && !path.contains('?')
}

/// Check if a route is language-specific (localized path, not /lang/ prefix
/// format)
pub fn is_lang_specific_route(path: &str, lang: &str) -> bool {
    let config = load_routes_config();
    let language_registry = get_language_registry();
    let default_language = language_registry.get_default_language();

    // Check if this path exists in the configuration for the specified language
    // and is NOT in the /lang/... prefix format AND is NOT the default language
    config.routes.iter().any(|route| {
        route.get_enabled()
            && (route.path == path || path_matches_pattern(&route.path, path))
            && route.get_language() == Some(lang.to_string())
            && lang != default_language
            && !path.starts_with(&format!("/{lang}/"))
    })
}

/// Check if a path matches a route pattern (supports {param} placeholders)
fn path_matches_pattern(pattern: &str, actual_path: &str) -> bool {
    // Simple pattern matching for {category}, {slug} etc.
    let pattern_parts: Vec<&str> = pattern.split('/').collect();
    let path_parts: Vec<&str> = actual_path.split('/').collect();

    if pattern_parts.len() != path_parts.len() {
        return false;
    }

    for (pattern_part, path_part) in pattern_parts.iter().zip(path_parts.iter()) {
        if pattern_part.starts_with('{') && pattern_part.ends_with('}') {
            // This is a parameter placeholder - it matches any value
            continue;
        } else if pattern_part != path_part {
            return false;
        }
    }

    true
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_is_valid_route_format() {
        assert!(is_valid_route_format("/"));
        assert!(is_valid_route_format("/blog"));
        assert!(is_valid_route_format("/blog/post"));

        assert!(!is_valid_route_format("blog")); // No leading slash
        assert!(!is_valid_route_format("/blog//post")); // Double slash
        assert!(!is_valid_route_format("/blog?param=value")); // Query params
    }

    #[test]
    fn test_path_matches_pattern() {
        assert!(path_matches_pattern("/blog/{category}", "/blog/tech"));
        assert!(path_matches_pattern(
            "/blog/{category}/{slug}",
            "/blog/tech/article"
        ));
        assert!(!path_matches_pattern("/blog/{category}", "/different/tech"));
        assert!(!path_matches_pattern(
            "/blog/{category}/{slug}",
            "/blog/tech"
        )); // Wrong number of parts
    }

    #[test]
    fn test_route_info_creation() {
        let info = RouteInfo::from_path("/blog");
        assert_eq!(info.path, "/blog");
        assert!(!info.language.is_empty());
        assert!(!info.clean_path.is_empty());
    }
}
