use super::flow::QuestionFlow;
use super::Survey;
use crate::context::ContextSession;
use crate::question_and_answers::{PostQuestionHook, Question};
use anyhow::Result;
use std::collections::HashMap;

pub struct SurveyBuilder {
    questions: HashMap<String, Question>,
    flows: HashMap<String, QuestionFlow<String>>,
    first_question: Option<String>,
}

impl SurveyBuilder {
    pub fn new(context_session: ContextSession) -> Self {
        Self {
            questions: HashMap::new(),
            flows: HashMap::new(),
            first_question: None,
        }
    }

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

    pub fn add_text_question(
        &mut self,
        question: &str,
        help_message: Option<&str>,
        initial_value: Option<&str>,
    ) -> &mut Self {
        let mut q = Question::ask(question);
        if let Some(help) = help_message {
            q = q.with_help(help);
        }
        if let Some(value) = initial_value {
            q = q.with_initial_value(value);
        }
        self.questions.insert(q.id(), q);
        self
    }

    pub fn add_text_question_with_hook(
        &mut self,
        question: &str,
        help_message: Option<&str>,
        initial_value: Option<&str>,
        hook: PostQuestionHook,
    ) -> &mut Self {
        let mut q = Question::ask(question);
        if let Some(help) = help_message {
            q = q.with_help(help);
        }
        if let Some(value) = initial_value {
            q = q.with_initial_value(value);
        }
        q = q.with_hook(hook);
        self.questions.insert(q.id(), q);
        self
    }

    pub fn add_select_question(
        &mut self,
        question: &str,
        options: Vec<impl Into<String>>,
    ) -> &mut Self {
        let q = Question::select(question, options);
        self.questions.insert(q.id(), q);
        self
    }

    pub fn add_select_question_with_hook(
        &mut self,
        question: &str,
        options: Vec<impl Into<String>>,
        hook: PostQuestionHook,
    ) -> &mut Self {
        let q = Question::select(question, options).with_hook(hook);
        self.questions.insert(q.id(), q);
        self
    }

    pub fn add_confirm_question(
        &mut self,
        question: &str,
        default_value: Option<bool>,
        help_message: Option<&str>,
    ) -> &mut Self {
        let mut q = Question::confirm(question);
        if let Some(default) = default_value {
            q = q.with_confirm_default(default);
        }
        if let Some(help) = help_message {
            q = q.with_help(help);
        }
        self.questions.insert(q.id(), q);
        self
    }

    pub fn add_confirm_question_with_hook(
        &mut self,
        question: &str,
        default_value: Option<bool>,
        help_message: Option<&str>,
        hook: PostQuestionHook,
    ) -> &mut Self {
        let mut q = Question::confirm(question);
        if let Some(default) = default_value {
            q = q.with_confirm_default(default);
        }
        if let Some(help) = help_message {
            q = q.with_help(help);
        }
        q = q.with_hook(hook);
        self.questions.insert(q.id(), q);
        self
    }

    pub fn set_first_question(&mut self, id: String) -> &mut Self {
        self.first_question = Some(id);
        self
    }

    pub fn add_flow(&mut self, from: String, flow: QuestionFlow<String>) -> &mut Self {
        self.flows.insert(from, flow);
        self
    }

    pub fn build(self) -> Result<Survey> {
        // Validate the survey structure
        let first_id = self
            .first_question
            .ok_or_else(|| anyhow::anyhow!("No first question specified"))?;

        // Validate all referenced questions exist
        for flow in self.flows.values() {
            for condition in &flow.conditions {
                if !self.questions.contains_key(&condition.next) {
                    return Err(anyhow::anyhow!(
                        "Flow references non-existent question: {:?}",
                        condition.next
                    ));
                }
            }
            if let Some(default) = &flow.default {
                if !self.questions.contains_key(default) {
                    return Err(anyhow::anyhow!(
                        "Default flow references non-existent question: {:?}",
                        default
                    ));
                }
            }
        }

        Survey::new(first_id, self.questions, self.flows)
    }
}
