//! Virtual Resolution System
//!
//! This module provides a layered override system for resolving pages and
//! components:
//! 1. Local implementations (highest priority)
//! 2. Local overrides
//! 3. Feature-specific overrides
//! 4. Foundation items (discovered dynamically)
//! 5. Auto-generated boilerplate (fallback)
//!
//! This enables using foundation items without local cloning while allowing
//! safe customization.

use std::collections::HashMap;
use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::build::foundation_discovery::{
    discover_foundation_items, FoundationDiscovery, FoundationItem, FoundationItemType,
};
use crate::build::path_resolution::get_manifest_resolver;

/// Result of virtual resolution
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VirtualResolution {
    /// The resolved item
    pub item: ResolvedItem,
    /// Resolution path used (for debugging/tracing)
    pub resolution_path: Vec<ResolutionStep>,
    /// Whether generation is needed
    pub needs_generation: bool,
}

/// A resolved item that can be used in the build
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolvedItem {
    /// Name of the item
    pub name: String,
    /// Type of the resolved item
    pub item_type: ResolvedItemType,
    /// Source of the resolution
    pub source: ResolutionSource,
    /// File path (either existing or where to generate)
    pub file_path: PathBuf,
    /// Generated import path for use in code
    pub import_path: String,
    /// Rust code content (if generated)
    pub content: Option<String>,
    /// Dependencies that need to be resolved
    pub dependencies: Vec<String>,
}

/// Type of resolved item
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ResolvedItemType {
    Page,
    Component,
    Utility,
    Type,
}

/// Source of resolution
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ResolutionSource {
    /// Found in local crates (highest priority)
    Local,
    /// Found in local overrides directory
    LocalOverride,
    /// Found in feature override
    FeatureOverride(String),
    /// Found in foundation
    Foundation,
    /// Auto-generated boilerplate
    Generated,
}

/// A step in the resolution process
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResolutionStep {
    pub source: ResolutionSource,
    pub path_checked: PathBuf,
    pub found: bool,
    pub reason: String,
}

/// Virtual resolver for pages and components
pub struct VirtualResolver {
    manifest_root: PathBuf,
    foundation_discovery: FoundationDiscovery,
    local_pages_path: PathBuf,
    local_components_path: PathBuf,
    override_pages_path: PathBuf,
    override_components_path: PathBuf,
}

impl VirtualResolver {
    /// Create a new virtual resolver
    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
        let manifest = get_manifest_resolver()?;
        let manifest_root = manifest.root().to_path_buf();

        // Get foundation discovery
        let foundation_discovery = discover_foundation_items()?;

        // PAP-compliant paths from manifest
        let local_pages_path = manifest_root.join("crates/pages/src");
        let local_components_path = manifest_root.join("crates/components/src");
        let override_pages_path = manifest_root.join("overrides/pages");
        let override_components_path = manifest_root.join("overrides/components");

        Ok(Self {
            manifest_root,
            foundation_discovery,
            local_pages_path,
            local_components_path,
            override_pages_path,
            override_components_path,
        })
    }

    /// Resolve a page with layered override system
    pub fn resolve_page(
        &self,
        page_name: &str,
    ) -> Result<VirtualResolution, Box<dyn std::error::Error>> {
        self.resolve_item(page_name, ResolvedItemType::Page)
    }

    /// Resolve a component with layered override system
    pub fn resolve_component(
        &self,
        component_name: &str,
    ) -> Result<VirtualResolution, Box<dyn std::error::Error>> {
        self.resolve_item(component_name, ResolvedItemType::Component)
    }

    /// Resolve any item using layered override system
    pub fn resolve_item(
        &self,
        item_name: &str,
        item_type: ResolvedItemType,
    ) -> Result<VirtualResolution, Box<dyn std::error::Error>> {
        let mut resolution_path = Vec::new();

        // Layer 1: Check local implementation (highest priority)
        if let Some(resolved) =
            self.check_local_implementation(item_name, &item_type, &mut resolution_path)
        {
            return Ok(VirtualResolution {
                item: resolved,
                resolution_path,
                needs_generation: false,
            });
        }

        // Layer 2: Check local overrides
        if let Some(resolved) =
            self.check_local_overrides(item_name, &item_type, &mut resolution_path)
        {
            return Ok(VirtualResolution {
                item: resolved,
                resolution_path,
                needs_generation: false,
            });
        }

        // Layer 3: Check feature overrides (TODO: implement when features are
        // available) if let Some(resolved) =
        // self.check_feature_overrides(item_name, &item_type, &mut resolution_path) {
        //     return Ok(VirtualResolution {
        //         item: resolved,
        //         resolution_path,
        //         needs_generation: false,
        //     });
        // }

        // Layer 4: Check foundation items
        if let Some(resolved) =
            self.check_foundation_items(item_name, &item_type, &mut resolution_path)
        {
            return Ok(VirtualResolution {
                item: resolved,
                resolution_path,
                needs_generation: true, // Need to generate import wrapper
            });
        }

        // Layer 5: Generate boilerplate (fallback)
        let resolved = self.generate_boilerplate(item_name, &item_type, &mut resolution_path);
        Ok(VirtualResolution {
            item: resolved,
            resolution_path,
            needs_generation: true,
        })
    }

    /// Check for local implementation in crates/pages or crates/components
    fn check_local_implementation(
        &self,
        item_name: &str,
        item_type: &ResolvedItemType,
        resolution_path: &mut Vec<ResolutionStep>,
    ) -> Option<ResolvedItem> {
        let (search_path, import_base) = match item_type {
            ResolvedItemType::Page => (&self.local_pages_path, "crate::pages"),
            ResolvedItemType::Component => (&self.local_components_path, "crate::components"),
            _ => return None, // Only pages and components have local implementations
        };

        let file_path = search_path.join(format!("{}.rs", item_name.to_lowercase()));
        let step = ResolutionStep {
            source: ResolutionSource::Local,
            path_checked: file_path.clone(),
            found: file_path.exists(),
            reason: "Checking for local implementation".to_string(),
        };

        if step.found {
            let import_path = format!("{}::{}", import_base, item_name);

            resolution_path.push(step);
            Some(ResolvedItem {
                name: item_name.to_string(),
                item_type: item_type.clone(),
                source: ResolutionSource::Local,
                file_path,
                import_path,
                content: None, // Existing file, no generation needed
                dependencies: Vec::new(),
            })
        } else {
            resolution_path.push(step);
            None
        }
    }

    /// Check for local overrides in overrides/pages or overrides/components
    fn check_local_overrides(
        &self,
        item_name: &str,
        item_type: &ResolvedItemType,
        resolution_path: &mut Vec<ResolutionStep>,
    ) -> Option<ResolvedItem> {
        let search_path = match item_type {
            ResolvedItemType::Page => &self.override_pages_path,
            ResolvedItemType::Component => &self.override_components_path,
            _ => return None,
        };

        let file_path = search_path.join(format!("{}.rs", item_name.to_lowercase()));
        let step = ResolutionStep {
            source: ResolutionSource::LocalOverride,
            path_checked: file_path.clone(),
            found: file_path.exists(),
            reason: "Checking for local override".to_string(),
        };

        if step.found {
            // For overrides, we need to copy/move to the appropriate crate location
            let target_path = match item_type {
                ResolvedItemType::Page => self
                    .local_pages_path
                    .join(format!("{}.rs", item_name.to_lowercase())),
                ResolvedItemType::Component => self
                    .local_components_path
                    .join(format!("{}.rs", item_name.to_lowercase())),
                _ => return None,
            };

            let import_path = match item_type {
                ResolvedItemType::Page => format!("crate::pages::{}", item_name),
                ResolvedItemType::Component => format!("crate::components::{}", item_name),
                _ => return None,
            };

            resolution_path.push(step);
            Some(ResolvedItem {
                name: item_name.to_string(),
                item_type: item_type.clone(),
                source: ResolutionSource::LocalOverride,
                file_path: target_path,
                import_path,
                content: std::fs::read_to_string(&file_path).ok(),
                dependencies: Vec::new(),
            })
        } else {
            resolution_path.push(step);
            None
        }
    }

    /// Check foundation items for the requested item
    fn check_foundation_items(
        &self,
        item_name: &str,
        item_type: &ResolvedItemType,
        resolution_path: &mut Vec<ResolutionStep>,
    ) -> Option<ResolvedItem> {
        let foundation_type = match item_type {
            ResolvedItemType::Page => FoundationItemType::Page,
            ResolvedItemType::Component => FoundationItemType::Component,
            ResolvedItemType::Utility => FoundationItemType::Utility,
            ResolvedItemType::Type => FoundationItemType::Type,
        };

        let step = ResolutionStep {
            source: ResolutionSource::Foundation,
            path_checked: self.foundation_discovery.foundation_root.clone(),
            found: false,
            reason: format!(
                "Searching foundation for {} {}",
                format!("{:?}", foundation_type).to_lowercase(),
                item_name
            ),
        };

        // Search in the appropriate foundation collection
        let found_item = match foundation_type {
            FoundationItemType::Page => self.foundation_discovery.pages.get(item_name),
            FoundationItemType::Component => self.foundation_discovery.components.get(item_name),
            FoundationItemType::Utility => self.foundation_discovery.utilities.get(item_name),
            FoundationItemType::Type => self.foundation_discovery.types.get(item_name),
        };

        if let Some(foundation_item) = found_item {
            let mut step = step;
            step.found = true;
            step.path_checked = foundation_item.file_path.clone();
            resolution_path.push(step);

            // Generate wrapper code for foundation item
            let wrapper_content = self.generate_foundation_wrapper(foundation_item, item_type);

            let target_path = match item_type {
                ResolvedItemType::Page => self
                    .local_pages_path
                    .join(format!("{}.rs", item_name.to_lowercase())),
                ResolvedItemType::Component => self
                    .local_components_path
                    .join(format!("{}.rs", item_name.to_lowercase())),
                _ => return None, // Utilities and types don't need wrappers in pages/components
            };

            let import_path = match item_type {
                ResolvedItemType::Page => format!("crate::pages::{}", item_name),
                ResolvedItemType::Component => format!("crate::components::{}", item_name),
                _ => foundation_item.module_path.clone(),
            };

            Some(ResolvedItem {
                name: item_name.to_string(),
                item_type: item_type.clone(),
                source: ResolutionSource::Foundation,
                file_path: target_path,
                import_path,
                content: Some(wrapper_content),
                dependencies: foundation_item.dependencies.clone(),
            })
        } else {
            resolution_path.push(step);
            None
        }
    }

    /// Generate boilerplate code when nothing else is found
    fn generate_boilerplate(
        &self,
        item_name: &str,
        item_type: &ResolvedItemType,
        resolution_path: &mut Vec<ResolutionStep>,
    ) -> ResolvedItem {
        let target_path = match item_type {
            ResolvedItemType::Page => self
                .local_pages_path
                .join(format!("{}.rs", item_name.to_lowercase())),
            ResolvedItemType::Component => self
                .local_components_path
                .join(format!("{}.rs", item_name.to_lowercase())),
            _ => self
                .manifest_root
                .join("src")
                .join(format!("{}.rs", item_name.to_lowercase())),
        };

        let content = match item_type {
            ResolvedItemType::Page => self.generate_page_boilerplate(item_name),
            ResolvedItemType::Component => self.generate_component_boilerplate(item_name),
            ResolvedItemType::Utility => self.generate_utility_boilerplate(item_name),
            ResolvedItemType::Type => self.generate_type_boilerplate(item_name),
        };

        let import_path = match item_type {
            ResolvedItemType::Page => format!("crate::pages::{}", item_name),
            ResolvedItemType::Component => format!("crate::components::{}", item_name),
            _ => format!("crate::{}", item_name),
        };

        let step = ResolutionStep {
            source: ResolutionSource::Generated,
            path_checked: target_path.clone(),
            found: false,
            reason: format!(
                "Generating boilerplate {} {}",
                format!("{:?}", item_type).to_lowercase(),
                item_name
            ),
        };
        resolution_path.push(step);

        ResolvedItem {
            name: item_name.to_string(),
            item_type: item_type.clone(),
            source: ResolutionSource::Generated,
            file_path: target_path,
            import_path,
            content: Some(content),
            dependencies: Vec::new(),
        }
    }

    /// Generate wrapper code for foundation items
    fn generate_foundation_wrapper(
        &self,
        foundation_item: &FoundationItem,
        item_type: &ResolvedItemType,
    ) -> String {
        match item_type {
            ResolvedItemType::Page => {
                format!(
                    r#"//! {} page - Foundation wrapper
//!
//! This page uses the foundation implementation from: {}
//! Module: {}

use leptos::*;

// Re-export the foundation page
pub use {}::{};

// Optional: Add local customizations here
// You can override specific parts while keeping the foundation base
"#,
                    foundation_item.name,
                    foundation_item.file_path.display(),
                    foundation_item.module_path,
                    foundation_item.module_path,
                    foundation_item.name
                )
            }
            ResolvedItemType::Component => {
                format!(
                    r#"//! {} component - Foundation wrapper
//!
//! This component uses the foundation implementation from: {}
//! Module: {}
//! Documentation: {}

use leptos::*;

// Re-export the foundation component
pub use {}::{};

// Optional: Create a local variant with customizations
// #[component]
// pub fn {}Local(/* your props */) -> impl IntoView {{
//     view! {{
//         <{}/* your custom props *//>
//     }}
// }}
"#,
                    foundation_item.name,
                    foundation_item.file_path.display(),
                    foundation_item.module_path,
                    foundation_item
                        .documentation
                        .as_ref()
                        .unwrap_or(&"No documentation available".to_string()),
                    foundation_item.module_path,
                    foundation_item.name,
                    foundation_item.name,
                    foundation_item.name
                )
            }
            _ => {
                format!(
                    r#"//! {} - Foundation wrapper
//!
//! Re-exports from foundation: {}

pub use {}::*;
"#,
                    foundation_item.name, foundation_item.module_path, foundation_item.module_path
                )
            }
        }
    }

    /// Generate boilerplate page
    fn generate_page_boilerplate(&self, page_name: &str) -> String {
        format!(
            r#"//! {} page
//!
//! Generated boilerplate page. Customize as needed.

use leptos::*;

#[component]
pub fn {}Page() -> impl IntoView {{
    view! {{
        <div class="page-{}">
            <h1>"{}"</h1>
            <p>"This is a generated boilerplate page. Customize it to fit your needs."</p>
        </div>
    }}
}}
"#,
            page_name,
            page_name,
            page_name.to_lowercase(),
            page_name
        )
    }

    /// Generate boilerplate component
    fn generate_component_boilerplate(&self, component_name: &str) -> String {
        format!(
            r#"//! {} component
//!
//! Generated boilerplate component. Customize as needed.

use leptos::*;

#[component]
pub fn {}(
    #[prop(optional)] class: Option<String>,
    children: Children,
) -> impl IntoView {{
    let class = class.unwrap_or_default();

    view! {{
        <div class={{format!("component-{} {{}}", class)}}>
            <div class="component-header">
                <h3>"{}"</h3>
            </div>
            <div class="component-content">
                {{children()}}
            </div>
        </div>
    }}
}}
"#,
            component_name,
            component_name,
            component_name.to_lowercase(),
            component_name
        )
    }

    /// Generate boilerplate utility
    fn generate_utility_boilerplate(&self, utility_name: &str) -> String {
        format!(
            r#"//! {} utility
//!
//! Generated boilerplate utility. Customize as needed.

/// Generated utility function
pub fn {}() -> String {{
    "Generated utility function - customize as needed".to_string()
}}
"#,
            utility_name,
            utility_name.to_lowercase()
        )
    }

    /// Generate boilerplate type
    fn generate_type_boilerplate(&self, type_name: &str) -> String {
        format!(
            r#"//! {} type
//!
//! Generated boilerplate type. Customize as needed.

use serde::{{Deserialize, Serialize}};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct {} {{
    // Add your fields here
    pub id: u64,
    pub name: String,
}}

impl {} {{
    pub fn new(id: u64, name: String) -> Self {{
        Self {{ id, name }}
    }}
}}
"#,
            type_name, type_name, type_name
        )
    }

    /// Apply resolution by generating/copying files as needed
    pub fn apply_resolution(
        &self,
        resolution: &VirtualResolution,
    ) -> Result<(), Box<dyn std::error::Error>> {
        if !resolution.needs_generation {
            return Ok(()); // Already exists, no generation needed
        }

        // Ensure parent directory exists
        if let Some(parent) = resolution.item.file_path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        // Generate or copy the file
        match &resolution.item.content {
            Some(content) => {
                std::fs::write(&resolution.item.file_path, content)?;
                println!(
                    "  ✅ Generated {} at: {}",
                    format!("{:?}", resolution.item.item_type).to_lowercase(),
                    resolution.item.file_path.display()
                );
            }
            None => {
                // For local overrides, copy from override location
                if resolution.item.source == ResolutionSource::LocalOverride {
                    let source_path = match resolution.item.item_type {
                        ResolvedItemType::Page => self
                            .override_pages_path
                            .join(format!("{}.rs", resolution.item.name.to_lowercase())),
                        ResolvedItemType::Component => self
                            .override_components_path
                            .join(format!("{}.rs", resolution.item.name.to_lowercase())),
                        _ => return Ok(()),
                    };

                    if source_path.exists() {
                        std::fs::copy(&source_path, &resolution.item.file_path)?;
                        println!(
                            "  ✅ Copied override {} from: {} to: {}",
                            format!("{:?}", resolution.item.item_type).to_lowercase(),
                            source_path.display(),
                            resolution.item.file_path.display()
                        );
                    }
                }
            }
        }

        Ok(())
    }
}
