use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, error, info};
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Skill {
    pub id: String,
    pub name: String,
    pub description: String,
    pub category: SkillCategory,
    pub parameters: Vec<SkillParameter>,
    pub steps: Vec<SkillStep>,
    pub tags: Vec<String>,
    pub author: String,
    pub version: String,
    pub created_at: chrono::DateTime<chrono::Utc>,
    pub public: bool,
    pub rating: f32,
    pub usage_count: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum SkillCategory {
    Navigation,
    FormFilling,
    DataExtraction,
    Testing,
    Automation,
    Security,
    Ecommerce,
    SocialMedia,
    General,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillParameter {
    pub name: String,
    pub description: String,
    pub parameter_type: ParameterType,
    pub required: bool,
    pub default_value: Option<serde_json::Value>,
    pub validation: Option<ParameterValidation>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ParameterType {
    String,
    Number,
    Boolean,
    Array,
    Object,
    Url,
    Selector,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParameterValidation {
    pub min_length: Option<usize>,
    pub max_length: Option<usize>,
    pub pattern: Option<String>,
    pub allowed_values: Option<Vec<serde_json::Value>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillStep {
    pub id: String,
    pub name: String,
    pub action: StepAction,
    pub parameters: HashMap<String, serde_json::Value>,
    pub timeout_ms: u32,
    pub retry_count: u32,
    pub on_failure: StepFailureAction,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StepAction {
    Navigate,
    Click,
    Type,
    Wait,
    Screenshot,
    ExecuteScript,
    ExtractText,
    ExtractAttribute,
    Scroll,
    WaitForElement,
    Condition,
    Loop,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StepFailureAction {
    Stop,
    Continue,
    Retry,
    Skip,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillExecution {
    pub id: String,
    pub skill_id: String,
    pub agent_id: String,
    pub session_id: String,
    pub parameters: HashMap<String, serde_json::Value>,
    pub status: ExecutionStatus,
    pub started_at: chrono::DateTime<chrono::Utc>,
    pub completed_at: Option<chrono::DateTime<chrono::Utc>>,
    pub current_step: Option<String>,
    pub step_results: HashMap<String, StepResult>,
    pub error: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ExecutionStatus {
    Pending,
    Running,
    Completed,
    Failed,
    Cancelled,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepResult {
    pub step_id: String,
    pub success: bool,
    pub data: serde_json::Value,
    pub error: Option<String>,
    pub execution_time_ms: u64,
    pub screenshot: Option<String>,
}

pub struct SkillEngine {
    skills: Arc<RwLock<HashMap<String, Skill>>>,
    executions: Arc<RwLock<HashMap<String, SkillExecution>>>,
}

impl Default for SkillEngine {
    fn default() -> Self {
        Self::new()
    }
}

impl SkillEngine {
    pub fn new() -> Self {
        Self {
            skills: Arc::new(RwLock::new(HashMap::new())),
            executions: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    pub async fn register_skill(&self, skill: Skill) -> Result<(), anyhow::Error> {
        let mut skills = self.skills.write().await;

        if skills.contains_key(&skill.id) {
            return Err(anyhow::anyhow!("Skill with ID {} already exists", skill.id));
        }

        skills.insert(skill.id.clone(), skill.clone());
        info!("Registered skill: {} (ID: {})", skill.name, skill.id);

        Ok(())
    }

    pub async fn get_skill(&self, skill_id: &str) -> Option<Skill> {
        let skills = self.skills.read().await;
        skills.get(skill_id).cloned()
    }

    pub async fn list_skills(
        &self,
        category: Option<SkillCategory>,
        tags: Option<Vec<String>>,
    ) -> Vec<Skill> {
        let skills = self.skills.read().await;

        skills
            .values()
            .filter(|skill| {
                let category_match = category.as_ref().is_none_or(|c| skill.category == *c);
                let tags_match = tags
                    .as_ref()
                    .is_none_or(|t| t.iter().all(|tag| skill.tags.contains(tag)));
                category_match && tags_match
            })
            .cloned()
            .collect()
    }

    pub async fn search_skills(&self, query: &str) -> Vec<Skill> {
        let skills = self.skills.read().await;
        let query_lower = query.to_lowercase();

        skills
            .values()
            .filter(|skill| {
                skill.name.to_lowercase().contains(&query_lower)
                    || skill.description.to_lowercase().contains(&query_lower)
                    || skill
                        .tags
                        .iter()
                        .any(|tag| tag.to_lowercase().contains(&query_lower))
            })
            .cloned()
            .collect()
    }

    pub async fn execute_skill(
        &self,
        skill_id: &str,
        agent_id: String,
        session_id: String,
        parameters: HashMap<String, serde_json::Value>,
    ) -> Result<String, anyhow::Error> {
        let skill = {
            let skills = self.skills.read().await;
            skills
                .get(skill_id)
                .cloned()
                .ok_or_else(|| anyhow::anyhow!("Skill not found: {}", skill_id))?
        };

        // Validate parameters
        self.validate_skill_parameters(&skill, &parameters).await?;

        let execution_id = Uuid::new_v4().to_string();

        let execution = SkillExecution {
            id: execution_id.clone(),
            skill_id: skill_id.to_string(),
            agent_id,
            session_id,
            parameters,
            status: ExecutionStatus::Pending,
            started_at: chrono::Utc::now(),
            completed_at: None,
            current_step: None,
            step_results: HashMap::new(),
            error: None,
        };

        {
            let mut executions = self.executions.write().await;
            executions.insert(execution_id.clone(), execution);
        }

        // Start execution in background
        let skill_clone = skill.clone();
        let executions_clone = Arc::clone(&self.executions);
        let execution_id_for_spawn = execution_id.clone();

        tokio::spawn(async move {
            if let Err(e) = Self::run_skill_execution(
                skill_clone,
                execution_id_for_spawn.clone(),
                executions_clone,
            )
            .await
            {
                error!("Skill execution {} failed: {}", execution_id_for_spawn, e);
            }
        });

        info!("Started skill execution: {}", execution_id);
        Ok(execution_id)
    }

    pub async fn get_execution(&self, execution_id: &str) -> Option<SkillExecution> {
        let executions = self.executions.read().await;
        executions.get(execution_id).cloned()
    }

    pub async fn cancel_execution(&self, execution_id: &str) -> Result<(), anyhow::Error> {
        let mut executions = self.executions.write().await;

        if let Some(execution) = executions.get_mut(execution_id) {
            match execution.status {
                ExecutionStatus::Pending | ExecutionStatus::Running => {
                    execution.status = ExecutionStatus::Cancelled;
                    execution.completed_at = Some(chrono::Utc::now());
                    info!("Cancelled skill execution: {}", execution_id);
                    Ok(())
                }
                _ => Err(anyhow::anyhow!(
                    "Cannot cancel execution in status: {:?}",
                    execution.status
                )),
            }
        } else {
            Err(anyhow::anyhow!("Execution not found: {}", execution_id))
        }
    }

    pub async fn list_executions(&self, agent_id: Option<&str>) -> Vec<SkillExecution> {
        let executions = self.executions.read().await;

        executions
            .values()
            .filter(|execution| agent_id.is_none_or(|id| execution.agent_id == id))
            .cloned()
            .collect()
    }

    async fn validate_skill_parameters(
        &self,
        skill: &Skill,
        parameters: &HashMap<String, serde_json::Value>,
    ) -> Result<(), anyhow::Error> {
        for param in &skill.parameters {
            let value = parameters.get(&param.name);

            if param.required && value.is_none() {
                return Err(anyhow::anyhow!(
                    "Required parameter '{}' is missing",
                    param.name
                ));
            }

            if let Some(value) = value {
                self.validate_parameter_value(param, value)?;
            }
        }

        Ok(())
    }

    fn validate_parameter_value(
        &self,
        param: &SkillParameter,
        value: &serde_json::Value,
    ) -> Result<(), anyhow::Error> {
        // Type validation
        match param.parameter_type {
            ParameterType::String => {
                if !value.is_string() {
                    return Err(anyhow::anyhow!(
                        "Parameter '{}' must be a string",
                        param.name
                    ));
                }
            }
            ParameterType::Number => {
                if !value.is_number() {
                    return Err(anyhow::anyhow!(
                        "Parameter '{}' must be a number",
                        param.name
                    ));
                }
            }
            ParameterType::Boolean => {
                if !value.is_boolean() {
                    return Err(anyhow::anyhow!(
                        "Parameter '{}' must be a boolean",
                        param.name
                    ));
                }
            }
            ParameterType::Array => {
                if !value.is_array() {
                    return Err(anyhow::anyhow!(
                        "Parameter '{}' must be an array",
                        param.name
                    ));
                }
            }
            ParameterType::Object => {
                if !value.is_object() {
                    return Err(anyhow::anyhow!(
                        "Parameter '{}' must be an object",
                        param.name
                    ));
                }
            }
            _ => {} // Skip validation for URL and Selector for now
        }

        // Additional validation
        if let Some(validation) = &param.validation {
            if let Some(string_value) = value.as_str() {
                if let Some(min_length) = validation.min_length {
                    if string_value.len() < min_length {
                        return Err(anyhow::anyhow!(
                            "Parameter '{}' must be at least {} characters",
                            param.name,
                            min_length
                        ));
                    }
                }

                if let Some(max_length) = validation.max_length {
                    if string_value.len() > max_length {
                        return Err(anyhow::anyhow!(
                            "Parameter '{}' must be at most {} characters",
                            param.name,
                            max_length
                        ));
                    }
                }

                if let Some(pattern) = &validation.pattern {
                    // Simple regex validation (in production, use regex crate)
                    if !string_value.contains(pattern) {
                        return Err(anyhow::anyhow!(
                            "Parameter '{}' does not match required pattern",
                            param.name
                        ));
                    }
                }
            }
        }

        Ok(())
    }

    async fn run_skill_execution(
        skill: Skill,
        execution_id: String,
        executions: Arc<RwLock<HashMap<String, SkillExecution>>>,
    ) -> Result<(), anyhow::Error> {
        // Update status to running
        {
            let mut execs = executions.write().await;
            if let Some(execution) = execs.get_mut(&execution_id) {
                execution.status = ExecutionStatus::Running;
            }
        }

        info!("Running skill execution: {}", execution_id);

        // Execute each step
        for step in &skill.steps {
            // Update current step
            {
                let mut execs = executions.write().await;
                if let Some(execution) = execs.get_mut(&execution_id) {
                    execution.current_step = Some(step.id.clone());
                }
            }

            debug!("Executing step: {}", step.name);

            let step_result = Self::execute_skill_step(step).await;

            // Store step result
            {
                let mut execs = executions.write().await;
                if let Some(execution) = execs.get_mut(&execution_id) {
                    execution
                        .step_results
                        .insert(step.id.clone(), step_result.clone());

                    if !step_result.success {
                        match step.on_failure {
                            StepFailureAction::Stop => {
                                execution.status = ExecutionStatus::Failed;
                                execution.error = step_result.error;
                                execution.completed_at = Some(chrono::Utc::now());
                                return Err(anyhow::anyhow!(
                                    "Skill execution failed at step: {}",
                                    step.name
                                ));
                            }
                            StepFailureAction::Skip => continue,
                            StepFailureAction::Retry => {
                                // Implement retry logic
                                continue;
                            }
                            StepFailureAction::Continue => continue,
                        }
                    }
                }
            }
        }

        // Mark as completed
        {
            let mut execs = executions.write().await;
            if let Some(execution) = execs.get_mut(&execution_id) {
                execution.status = ExecutionStatus::Completed;
                execution.completed_at = Some(chrono::Utc::now());
                execution.current_step = None;
            }
        }

        info!("Skill execution completed: {}", execution_id);
        Ok(())
    }

    async fn execute_skill_step(step: &SkillStep) -> StepResult {
        let start_time = std::time::Instant::now();

        debug!("Executing step action: {:?}", step.action);

        // Simulate step execution
        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;

        let execution_time = start_time.elapsed().as_millis() as u64;

        // In a real implementation, this would execute actual browser commands
        let (success, data, error) = match &step.action {
            StepAction::Navigate => {
                if let Some(url) = step.parameters.get("url") {
                    (true, url.clone(), None)
                } else {
                    (
                        false,
                        serde_json::Value::Null,
                        Some("Missing URL parameter".to_string()),
                    )
                }
            }
            StepAction::Click => {
                if let Some(selector) = step.parameters.get("selector") {
                    (true, serde_json::json!({"clicked": selector}), None)
                } else {
                    (
                        false,
                        serde_json::Value::Null,
                        Some("Missing selector parameter".to_string()),
                    )
                }
            }
            StepAction::Type => {
                if let Some(text) = step.parameters.get("text") {
                    (true, serde_json::json!({"typed": text}), None)
                } else {
                    (
                        false,
                        serde_json::Value::Null,
                        Some("Missing text parameter".to_string()),
                    )
                }
            }
            StepAction::Screenshot => (
                true,
                serde_json::json!({"screenshot": "base64_image_data"}),
                None,
            ),
            _ => (true, serde_json::json!({"result": "success"}), None),
        };

        StepResult {
            step_id: step.id.clone(),
            success,
            data,
            error,
            execution_time_ms: execution_time,
            screenshot: None,
        }
    }

    pub async fn get_skill_stats(&self) -> SkillStats {
        let skills = self.skills.read().await;
        let executions = self.executions.read().await;

        let total_skills = skills.len();
        let public_skills = skills.values().filter(|s| s.public).count();

        let category_counts = {
            let mut counts = HashMap::new();
            for skill in skills.values() {
                *counts.entry(format!("{:?}", skill.category)).or_insert(0) += 1;
            }
            counts
        };

        let total_executions = executions.len();
        let running_executions = executions
            .values()
            .filter(|e| e.status == ExecutionStatus::Running)
            .count();
        let completed_executions = executions
            .values()
            .filter(|e| e.status == ExecutionStatus::Completed)
            .count();
        let failed_executions = executions
            .values()
            .filter(|e| e.status == ExecutionStatus::Failed)
            .count();

        SkillStats {
            total_skills,
            public_skills,
            category_counts,
            total_executions,
            running_executions,
            completed_executions,
            failed_executions,
        }
    }

    // Initialize with some default skills
    pub async fn init_default_skills(&self) -> Result<(), anyhow::Error> {
        let default_skills = vec![
            Skill {
                id: "navigate-to-url".to_string(),
                name: "Navigate to URL".to_string(),
                description: "Navigate to a specific URL".to_string(),
                category: SkillCategory::Navigation,
                parameters: vec![SkillParameter {
                    name: "url".to_string(),
                    description: "URL to navigate to".to_string(),
                    parameter_type: ParameterType::Url,
                    required: true,
                    default_value: None,
                    validation: None,
                }],
                steps: vec![SkillStep {
                    id: "step1".to_string(),
                    name: "Navigate to URL".to_string(),
                    action: StepAction::Navigate,
                    parameters: HashMap::from([(
                        "url".to_string(),
                        serde_json::Value::String("${url}".to_string()),
                    )]),
                    timeout_ms: 10000,
                    retry_count: 3,
                    on_failure: StepFailureAction::Stop,
                }],
                tags: vec!["navigation".to_string(), "basic".to_string()],
                author: "Ditto Team".to_string(),
                version: "1.0.0".to_string(),
                created_at: chrono::Utc::now(),
                public: true,
                rating: 5.0,
                usage_count: 0,
            },
            Skill {
                id: "take-screenshot".to_string(),
                name: "Take Screenshot".to_string(),
                description: "Take a screenshot of the current page".to_string(),
                category: SkillCategory::Testing,
                parameters: vec![SkillParameter {
                    name: "filename".to_string(),
                    description: "Filename for the screenshot".to_string(),
                    parameter_type: ParameterType::String,
                    required: false,
                    default_value: Some(serde_json::Value::String("screenshot.png".to_string())),
                    validation: None,
                }],
                steps: vec![SkillStep {
                    id: "step1".to_string(),
                    name: "Take screenshot".to_string(),
                    action: StepAction::Screenshot,
                    parameters: HashMap::from([(
                        "filename".to_string(),
                        serde_json::Value::String("${filename}".to_string()),
                    )]),
                    timeout_ms: 5000,
                    retry_count: 2,
                    on_failure: StepFailureAction::Stop,
                }],
                tags: vec!["screenshot".to_string(), "testing".to_string()],
                author: "Ditto Team".to_string(),
                version: "1.0.0".to_string(),
                created_at: chrono::Utc::now(),
                public: true,
                rating: 4.5,
                usage_count: 0,
            },
        ];

        for skill in default_skills {
            self.register_skill(skill).await?;
        }

        info!("Initialized default skills");
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkillStats {
    pub total_skills: usize,
    pub public_skills: usize,
    pub category_counts: HashMap<String, usize>,
    pub total_executions: usize,
    pub running_executions: usize,
    pub completed_executions: usize,
    pub failed_executions: usize,
}
