pub mod builder;
pub mod flow;
pub mod utils;

use crate::context::ContextSession;
use crate::question_and_answers::Question;
use anyhow::Result;
use flow::QuestionFlow;
use std::collections::HashMap;

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

impl Survey {
    pub fn new(
        first_question: String,
        questions: HashMap<String, Question>,
        flows: HashMap<String, QuestionFlow<String>>,
    ) -> Result<Self> {
        Ok(Self {
            first_question,
            questions,
            flows,
        })
    }

    pub fn run(&mut self, session: &mut ContextSession) {
        let mut current_id = self.first_question.clone();
        let context = session.get_context().clone();

        while let Some(question) = self.questions.get_mut(&current_id) {
            // Check for existing answer first
            if let Some(existing_answer) = context.get_response(question.question_type.get_question()) {
                // Use existing answer for flow
                if let Some(flow) = self.flows.get(&current_id) {
                    let next_id = flow
                        .conditions
                        .iter()
                        .find(|c| c.answer == *existing_answer)
                        .map(|c| c.next.clone())
                        .or_else(|| flow.default.clone());

                    match next_id {
                        Some(id) => current_id = id,
                        None => break,
                    }
                }
                continue;
            }

            // Get answer from user with retry logic
            let mut answer = None;
            let mut attempts = 0;

            let max_retries = question.retry_config.as_ref().map_or(1, |rc| rc.max_retries);
            
            while answer.is_none() && attempts < max_retries {
                match question.get_answer_with_context(session) {
                    Ok(ans) => {
                        // Run the hook if it exists
                        if let Some(hook) = &mut question.post_question_hook {
                            match hook(&ans, &context, session) {
                                Ok(_) => {
                                    // Check retry condition regardless of hook result
                                    if let Some(retry_config) = &question.retry_config {
                                        match (retry_config.should_retry)(session) {
                                            Ok(should_retry) => {
                                                if !should_retry {
                                                    answer = Some(ans);
                                                } else {
                                                    attempts += 1;
                                                    if attempts >= max_retries {
                                                        eprintln!("Max retries ({}) reached", max_retries);
                                                        answer = Some(ans); // Use last answer if max retries reached
                                                    }
                                                }
                                            }
                                            Err(e) => {
                                                eprintln!("Error checking retry condition: {}", e);
                                                attempts += 1;
                                            }
                                        }
                                    } else {
                                        answer = Some(ans);
                                    }
                                }
                                Err(e) => {
                                    eprintln!("Error in post-question hook: {}", e);
                                    attempts += 1;
                                }
                            }
                        } else {
                            // No hook, just check retry condition if it exists
                            if let Some(retry_config) = &question.retry_config {
                                match (retry_config.should_retry)(session) {
                                    Ok(should_retry) => {
                                        if !should_retry {
                                            answer = Some(ans);
                                        } else {
                                            attempts += 1;
                                            if attempts >= max_retries {
                                                eprintln!("Max retries ({}) reached", max_retries);
                                                answer = Some(ans); // Use last answer if max retries reached
                                            }
                                        }
                                    }
                                    Err(e) => {
                                        eprintln!("Error checking retry condition: {}", e);
                                        attempts += 1;
                                    }
                                }
                            } else {
                                answer = Some(ans);
                            }
                        }
                    }
                    Err(e) => {
                        eprintln!("Error getting answer: {}", e);
                        attempts += 1;
                    }
                }
            }

            let answer = answer.unwrap_or_else(|| "No answer provided".to_string());

            // Save answer to context
            session.add_text_to_context(question.question_type.get_question(), &answer);
            if let Err(e) = session.save_context() {
                eprintln!("Failed to save context: {}", e);
            }

            // Find next question using flows
            if let Some(flow) = self.flows.get(&current_id) {
                let next_id = flow
                    .conditions
                    .iter()
                    .find(|c| c.answer == answer)
                    .map(|c| c.next.clone())
                    .or_else(|| flow.default.clone());

                match next_id {
                    Some(id) => current_id = id,
                    None => break,
                }
            } else {
                break;
            }
        }
    }

   
}