use std::collections::HashMap;

use regex::Regex;

/// Questionnaire Engine
///
/// Manages questionnaire flow, validation, and response collection.
/// Orchestrates decision tree with questions to create interactive sessions.
///
/// Features:
/// - Question definition and validation
/// - Conditional question display
/// - Answer validation
/// - Response tracking
/// - Session state management
use super::decision_tree::{DecisionTree, Expression};

/// Question type
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum QuestionKind {
    Text,
    Select,
    Multiselect,
    Confirm,
    Number,
}

impl QuestionKind {
    pub fn as_str(&self) -> &'static str {
        match self {
            Self::Text => "text",
            Self::Select => "select",
            Self::Multiselect => "multiselect",
            Self::Confirm => "confirm",
            Self::Number => "number",
        }
    }

    pub fn parse(s: &str) -> Option<Self> {
        match s {
            "text" => Some(Self::Text),
            "select" => Some(Self::Select),
            "multiselect" => Some(Self::Multiselect),
            "confirm" => Some(Self::Confirm),
            "number" => Some(Self::Number),
            _ => None,
        }
    }
}

/// Validation rule for answer
#[derive(Debug, Clone)]
pub struct ValidationRule {
    pub required: bool,
    pub pattern: Option<String>,
    pub min_value: Option<i32>,
    pub max_value: Option<i32>,
    pub choices: Option<Vec<String>>,
}

impl ValidationRule {
    pub fn new() -> Self {
        Self {
            required: false,
            pattern: None,
            min_value: None,
            max_value: None,
            choices: None,
        }
    }

    pub fn required(mut self, required: bool) -> Self {
        self.required = required;
        self
    }

    pub fn pattern(mut self, pattern: impl Into<String>) -> Self {
        self.pattern = Some(pattern.into());
        self
    }

    pub fn choices(mut self, choices: Vec<String>) -> Self {
        self.choices = Some(choices);
        self
    }

    pub fn validate(&self, value: &str) -> Result<(), String> {
        if self.required && value.is_empty() {
            return Err("This field is required".to_string());
        }

        if let Some(pattern) = &self.pattern {
            if let Ok(regex) = Regex::new(pattern) {
                if !regex.is_match(value) {
                    return Err(format!("Value must match pattern: {}", pattern));
                }
            }
        }

        if let Some(choices) = &self.choices {
            if !choices.contains(&value.to_string()) {
                return Err(format!("Must choose from: {}", choices.join(", ")));
            }
        }

        Ok(())
    }
}

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

/// Single question in questionnaire
#[derive(Debug, Clone)]
pub struct Question {
    pub id: String,
    pub kind: QuestionKind,
    pub message: String,
    pub help: Option<String>,
    pub default: Option<String>,
    pub when: Option<Expression>,
    pub validation: ValidationRule,
    pub ai_suggest: bool,
}

impl Question {
    pub fn new(id: impl Into<String>, kind: QuestionKind, message: impl Into<String>) -> Self {
        Self {
            id: id.into(),
            kind,
            message: message.into(),
            help: None,
            default: None,
            when: None,
            validation: ValidationRule::new(),
            ai_suggest: false,
        }
    }

    pub fn with_help(mut self, help: impl Into<String>) -> Self {
        self.help = Some(help.into());
        self
    }

    pub fn with_default(mut self, default: impl Into<String>) -> Self {
        self.default = Some(default.into());
        self
    }

    pub fn with_condition(mut self, when: Expression) -> Self {
        self.when = Some(when);
        self
    }

    pub fn with_validation(mut self, validation: ValidationRule) -> Self {
        self.validation = validation;
        self
    }

    pub fn with_ai_suggest(mut self, enabled: bool) -> Self {
        self.ai_suggest = enabled;
        self
    }

    /// Check if question should be asked given current answers
    pub fn should_ask(&self, answers: &HashMap<String, String>) -> bool {
        if let Some(condition) = &self.when {
            condition.evaluate(answers)
        } else {
            true
        }
    }
}

/// User answer to a question
#[derive(Debug, Clone)]
pub struct Answer {
    pub question_id: String,
    pub value: String,
    pub timestamp: String,
}

impl Answer {
    pub fn new(question_id: impl Into<String>, value: impl Into<String>) -> Self {
        Self {
            question_id: question_id.into(),
            value: value.into(),
            timestamp: chrono::Utc::now().to_rfc3339(),
        }
    }
}

/// Complete questionnaire response
#[derive(Debug, Clone)]
pub struct QuestionnaireResponse {
    pub questionnaire_name: String,
    pub questionnaire_version: String,
    pub answers: HashMap<String, String>,
    pub completed: bool,
    pub completion_time: Option<String>,
}

impl QuestionnaireResponse {
    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
        Self {
            questionnaire_name: name.into(),
            questionnaire_version: version.into(),
            answers: HashMap::new(),
            completed: false,
            completion_time: None,
        }
    }

    pub fn add_answer(&mut self, question_id: impl Into<String>, value: impl Into<String>) {
        self.answers.insert(question_id.into(), value.into());
    }

    pub fn complete(mut self) -> Self {
        self.completed = true;
        self.completion_time = Some(chrono::Utc::now().to_rfc3339());
        self
    }
}

/// Questionnaire engine orchestrator
pub struct QuestionnaireEngine {
    name: String,
    version: String,
    questions: HashMap<String, Question>,
    decision_tree: DecisionTree,
}

impl QuestionnaireEngine {
    pub fn new(
        name: impl Into<String>,
        version: impl Into<String>,
        decision_tree: DecisionTree,
    ) -> Self {
        Self {
            name: name.into(),
            version: version.into(),
            questions: HashMap::new(),
            decision_tree,
        }
    }

    pub fn add_question(mut self, question: Question) -> Self {
        self.questions.insert(question.id.clone(), question);
        self
    }

    /// Get next question to ask
    pub fn get_next_question(
        &self,
        current_question: Option<&str>,
        current_answer: Option<&str>,
        answers: &HashMap<String, String>,
    ) -> Option<&Question> {
        let next_id = if let (Some(q_id), Some(answer)) = (current_question, current_answer) {
            self.decision_tree.get_next_question(q_id, answer)?
        } else {
            self.decision_tree.get_root().to_string()
        };

        if next_id == "END" {
            return None;
        }

        // Get the question
        let question = self.questions.get(&next_id)?;

        // Check if it should be asked
        if question.should_ask(answers) {
            Some(question)
        } else {
            // Skip to next question
            self.get_next_question(Some(&next_id), None, answers)
        }
    }

    /// Validate answer for question
    pub fn validate_answer(&self, question_id: &str, value: &str) -> Result<(), String> {
        let question = self
            .questions
            .get(question_id)
            .ok_or_else(|| "Question not found".to_string())?;

        question.validation.validate(value)
    }

    /// Get question by ID
    pub fn get_question(&self, id: &str) -> Option<&Question> {
        self.questions.get(id)
    }

    /// Check if questionnaire is complete
    pub fn is_complete(&self, answers: &HashMap<String, String>) -> bool {
        let mut current_question = Some(self.decision_tree.get_root());

        loop {
            let next = current_question.and_then(|q_id| {
                let question = self.questions.get(q_id)?;
                if question.should_ask(answers) {
                    Some(q_id)
                } else {
                    None
                }
            });

            if next.is_none() {
                // No more questions to ask
                return true;
            }

            current_question = next;

            let Some(q_id) = current_question else {
                return true;
            };

            let Some(answer) = answers.get(q_id) else {
                return false; // Question not answered
            };

            let next_q = self.get_next_question(Some(q_id), Some(answer), answers);
            if next_q.is_none() {
                return true;
            }
        }
    }

    pub fn questionnaire_name(&self) -> &str {
        &self.name
    }

    pub fn questionnaire_version(&self) -> &str {
        &self.version
    }
}

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

    #[test]
    fn test_question_kind_conversion() {
        assert_eq!(QuestionKind::Text.as_str(), "text");
        assert_eq!(QuestionKind::parse("select"), Some(QuestionKind::Select));
        assert_eq!(QuestionKind::parse("invalid"), None);
    }

    #[test]
    fn test_validation_required() {
        let rule = ValidationRule::new().required(true);
        assert!(rule.validate("").is_err());
        assert!(rule.validate("value").is_ok());
    }

    #[test]
    fn test_validation_pattern() {
        let rule = ValidationRule::new().pattern(r"^\d+$");
        assert!(rule.validate("123").is_ok());
        assert!(rule.validate("abc").is_err());
    }

    #[test]
    fn test_validation_choices() {
        let rule = ValidationRule::new().choices(vec!["prod".to_string(), "staging".to_string()]);
        assert!(rule.validate("prod").is_ok());
        assert!(rule.validate("dev").is_err());
    }

    #[test]
    fn test_question_conditional() {
        let question = Question::new("q1", QuestionKind::Text, "Deploy mode?")
            .with_condition(Expression::new("is_prod == 'true'"));

        let mut answers = HashMap::new();
        answers.insert("is_prod".to_string(), "true".to_string());
        assert!(question.should_ask(&answers));

        answers.insert("is_prod".to_string(), "false".to_string());
        assert!(!question.should_ask(&answers));
    }

    #[test]
    fn test_questionnaire_response() {
        let mut response = QuestionnaireResponse::new("Test", "1.0.0");
        response.add_answer("q1", "answer1");
        response.add_answer("q2", "answer2");

        assert_eq!(response.answers.get("q1"), Some(&"answer1".to_string()));
        assert_eq!(response.answers.len(), 2);

        let completed = response.complete();
        assert!(completed.completed);
        assert!(completed.completion_time.is_some());
    }
}
