use std::path::{Path, PathBuf};
use tokio::fs;

use crate::error::{K4000Error, K4000Result};
use crate::types::{Feature, ProjectConfig, Question, ReviewSession, TaskState, TaskSummary};

/// Standard K4000 workflow instructions appended to every plan.md
/// This ensures all projects using K4000 follow the same implementation flow.
pub const K4000_WORKFLOW_SECTION: &str = r#"
---

## K4000 Workflow (MANDATORY)

This section defines the required workflow for implementing this task. These steps are automatically enforced by K4000.

### Implementation Flow

1. **Feature-by-Feature Implementation**
   - Implement features in order (F1, F2, F3...)
   - Each feature must be fully complete before moving to the next
   - After completing each feature, call `mark_feature_complete(feature_id)`

2. **Acceptance Criteria Verification (AUTOMATIC)**
   - When `mark_feature_complete` is called, K4000 returns the acceptance criteria
   - You MUST verify each criterion before calling `mark_feature_verified`
   - The feature's `passes` field only becomes `true` after verification

3. **Code Review (AUTOMATIC - After Each Feature)**
   - After marking a feature verified, IMMEDIATELY start a code review
   - Call `set_task_status(status="reviewing", files=[...modified files...])`
   - Then call `create_review_session` with the files
   - Review from all 5 perspectives: bugs, best_practices, security, performance, simplification
   - Submit findings with `submit_findings`

4. **Fix and Re-Review Loop (AUTOMATIC)**
   - Fix all critical and high severity findings
   - Call `complete_iteration(start_next=true)` after fixing
   - Re-review the fixed code
   - Repeat until no critical/high findings remain
   - Call `approve_review` when clean

5. **Task Completion**
   - ALL features must have `passes=true`
   - A review session must exist and be approved
   - Only then can you call `set_task_status(status="done")`

### Review Perspectives Checklist

For each review, examine the code from these angles:

- [ ] **Bugs**: Logic errors, null/undefined handling, edge cases, race conditions
- [ ] **Best Practices**: Code style, naming, documentation, error handling patterns
- [ ] **Security**: Input validation, authentication, authorization, data exposure
- [ ] **Performance**: N+1 queries, unnecessary computation, memory leaks
- [ ] **Simplification**: Dead code, overly complex logic, duplication

### MANDATORY: Progress Logging

You MUST log progress during implementation using `log_progress`. This is required for context restoration when starting fresh sessions.

**When to log:**
- Starting work on a feature: `log_progress(feature_id="F1", message="Starting implementation")`
- Significant code changes: `log_progress(feature_id="F1", message="Added X to src/file.rs")`
- Completing a feature: `log_progress(feature_id="F1", message="Feature complete, tests passing")`
- Blockers or issues: `log_progress(message="Blocked: need X before continuing")`
- Important decisions: `log_progress(feature_id="F1", message="Chose X over Y because...")`

**Frequency:** Log at least once per significant code change or every 15-20 minutes of work.
"#;

/// Manages file-based storage for tasks and reviews.
///
/// Storage is project-local in `tasks/`:
/// ```text
/// project/
///   tasks/
///     .config/
///       config.toml        # Project configuration (feature_approval_mode, etc.)
///     my-task/
///       task.md            # User's original description
///       plan.md            # Implementation plan (K4000 writes)
///       feature-list.json  # Features (K4000 writes)
///       questions.json     # Q&A (K4000 writes)
///       state.json         # Task lifecycle state (K4000 writes)
///       progress.txt       # Progress log (K4000 writes)
///       .review/           # Review sessions (hidden)
///         review-*.json
/// ```
#[derive(Debug, Clone)]
pub struct StateManager {
    /// Project root path (contains tasks/ directory)
    project_path: PathBuf,
}

impl StateManager {
    /// Create a new StateManager for a project
    pub fn new(project_path: impl Into<PathBuf>) -> Self {
        Self {
            project_path: project_path.into(),
        }
    }

    /// Get the project path
    pub fn project_path(&self) -> &Path {
        &self.project_path
    }

    /// Get the tasks directory path (project/tasks/)
    pub fn tasks_dir(&self) -> PathBuf {
        self.project_path.join("tasks")
    }

    /// Get the config directory path (project/tasks/.config/)
    pub fn config_dir(&self) -> PathBuf {
        self.tasks_dir().join(".config")
    }

    /// Get the config file path (project/tasks/.config/config.toml)
    pub fn config_path(&self) -> PathBuf {
        self.config_dir().join("config.toml")
    }

    /// Get the old state directory path (project/tasks/.state/) - for migration
    pub fn old_state_dir(&self) -> PathBuf {
        self.tasks_dir().join(".state")
    }

    /// Get the old state directory for a specific task - for migration
    pub fn old_task_state_dir(&self, name: &str) -> PathBuf {
        self.old_state_dir().join(sanitize_name(name))
    }

    /// Get the task folder path (project/tasks/task-name/)
    pub fn task_folder(&self, name: &str) -> PathBuf {
        self.tasks_dir().join(sanitize_name(name))
    }

    /// Get the state.json path for a task (now in task folder)
    pub fn task_state_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("state.json")
    }

    /// Get the task.md path (user's original description - read only!)
    pub fn task_md_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("task.md")
    }

    /// Get the plan.md path (K4000 writes implementation plan + estimate)
    pub fn plan_md_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("plan.md")
    }

    /// Get the questions.json path (K4000 writes Q&A)
    pub fn questions_json_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("questions.json")
    }

    /// Get the feature-list.json path (K4000 writes feature list)
    pub fn feature_list_json_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("feature-list.json")
    }

    /// Get the progress.txt path (K4000 writes progress log)
    pub fn progress_txt_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("progress.txt")
    }

    /// Get the review directory path (hidden in task folder)
    pub fn review_dir(&self, task_name: &str) -> PathBuf {
        self.task_folder(task_name).join(".review")
    }

    /// Get the review JSON path for a task
    pub fn review_path(&self, task_name: &str, review_id: &str) -> PathBuf {
        self.review_dir(task_name)
            .join(format!("review-{}.json", sanitize_name(review_id)))
    }

    /// Ensure the tasks directory exists
    pub async fn ensure_dirs(&self) -> K4000Result<()> {
        fs::create_dir_all(self.tasks_dir()).await?;
        Ok(())
    }

    /// Ensure a task's folder exists
    pub async fn ensure_task_folder(&self, name: &str) -> K4000Result<()> {
        fs::create_dir_all(self.task_folder(name)).await?;
        Ok(())
    }

    /// Ensure a task's review directory exists
    pub async fn ensure_review_dir(&self, name: &str) -> K4000Result<()> {
        fs::create_dir_all(self.review_dir(name)).await?;
        Ok(())
    }

    /// Ensure the config directory exists
    pub async fn ensure_config_dir(&self) -> K4000Result<()> {
        fs::create_dir_all(self.config_dir()).await?;
        Ok(())
    }

    // ==================== Project Config Operations ====================

    /// Load project configuration from tasks/.config/config.toml
    ///
    /// Returns default config if the file doesn't exist.
    pub async fn load_project_config(&self) -> K4000Result<ProjectConfig> {
        let path = self.config_path();
        if !path.exists() {
            return Ok(ProjectConfig::default());
        }
        let content = fs::read_to_string(&path).await?;
        let config = ProjectConfig::from_toml(&content)
            .map_err(|e| K4000Error::ConfigError(format!("Failed to parse config.toml: {}", e)))?;
        Ok(config)
    }

    /// Save project configuration to tasks/.config/config.toml
    pub async fn save_project_config(&self, config: &ProjectConfig) -> K4000Result<()> {
        self.ensure_config_dir().await?;
        let path = self.config_path();
        let content = config
            .to_toml()
            .map_err(|e| K4000Error::ConfigError(format!("Failed to serialize config: {}", e)))?;
        fs::write(&path, content).await?;
        Ok(())
    }

    /// Check if project config file exists
    pub fn has_project_config(&self) -> bool {
        self.config_path().exists()
    }

    /// Ensure the project config is initialized with all settings.
    ///
    /// This method:
    /// - Creates config.toml with defaults if it doesn't exist
    /// - Adds missing settings to existing config (preserving existing values)
    /// - Returns the config and a message if the file was created or updated
    ///
    /// Returns: (ProjectConfig, Option<String>) where the Option contains a notification
    /// message if the config was created or updated.
    pub async fn ensure_config_initialized(&self) -> K4000Result<(ProjectConfig, Option<String>)> {
        let path = self.config_path();
        let defaults = ProjectConfig::default();

        if !path.exists() {
            // Create tasks/ directory first if it doesn't exist
            self.ensure_dirs().await?;
            // Create new config with comments
            self.ensure_config_dir().await?;
            let content = defaults.to_toml_with_comments();
            fs::write(&path, &content).await?;
            let message = format!("Created default config at {}", path.display());
            return Ok((defaults, Some(message)));
        }

        // Config exists - check if it needs updating
        let existing_content = fs::read_to_string(&path).await?;

        if ProjectConfig::has_all_settings(&existing_content) {
            // All settings present, just load and return
            let config = ProjectConfig::from_toml(&existing_content).map_err(|e| {
                K4000Error::ConfigError(format!("Failed to parse config.toml: {}", e))
            })?;
            return Ok((config, None));
        }

        // Missing settings - merge defaults while preserving existing values
        let merged_content = defaults.merge_with_existing(&existing_content);
        fs::write(&path, &merged_content).await?;

        let config = ProjectConfig::from_toml(&merged_content).map_err(|e| {
            K4000Error::ConfigError(format!("Failed to parse merged config.toml: {}", e))
        })?;

        let message = format!("Updated config at {} with missing settings", path.display());
        Ok((config, Some(message)))
    }

    // ==================== Task State Operations ====================

    /// Load a task's state (with automatic migration from old structure)
    pub async fn load_task_state(&self, name: &str) -> K4000Result<TaskState> {
        // Check if migration is needed
        if self.needs_migration(name) {
            self.migrate_task(name).await?;
        }

        let path = self.task_state_path(name);
        if !path.exists() {
            return Err(K4000Error::TaskNotFound(name.to_string()));
        }
        let content = fs::read_to_string(&path).await?;
        let state: TaskState = serde_json::from_str(&content)?;
        Ok(state)
    }

    /// Save a task's state
    pub async fn save_task_state(&self, name: &str, state: &TaskState) -> K4000Result<()> {
        self.ensure_task_folder(name).await?;
        let path = self.task_state_path(name);
        let content = serde_json::to_string_pretty(state)?;
        fs::write(&path, content).await?;
        Ok(())
    }

    /// Check if a task exists (has state.json in task folder)
    pub async fn task_exists(&self, name: &str) -> bool {
        // Check new location first, then old location
        self.task_state_path(name).exists()
            || self.old_task_state_dir(name).join("state.json").exists()
    }

    /// Delete a task's state and review data (not the whole task folder!)
    pub async fn delete_task_state(&self, name: &str) -> K4000Result<()> {
        // Delete state.json
        let state_path = self.task_state_path(name);
        if state_path.exists() {
            fs::remove_file(&state_path).await?;
        }

        // Delete .review directory
        let review_dir = self.review_dir(name);
        if review_dir.exists() {
            fs::remove_dir_all(&review_dir).await?;
        }

        Ok(())
    }

    /// List all tasks in the project (scans task folders for state.json)
    pub async fn list_tasks(&self) -> K4000Result<Vec<TaskSummary>> {
        let tasks_dir = self.tasks_dir();
        if !tasks_dir.exists() {
            return Ok(vec![]);
        }

        let mut summaries = Vec::new();
        let mut entries = fs::read_dir(&tasks_dir).await?;

        while let Some(entry) = entries.next_entry().await? {
            if entry.file_type().await?.is_dir() {
                let name = entry.file_name().to_string_lossy().to_string();
                // Skip hidden directories (.config, .state for migration)
                if name.starts_with('.') {
                    continue;
                }

                // Check if this task has state.json (either new or old location)
                if let Ok(state) = self.load_task_state(&name).await {
                    // Load questions to count unanswered
                    let questions = self.read_questions(&name).await.unwrap_or_default();
                    let unanswered = questions.iter().filter(|q| !q.answered).count();

                    summaries.push(TaskSummary::new(
                        &name,
                        state.status,
                        unanswered,
                        state.review_iterations,
                    ));
                }
            }
        }

        // Sort by name
        summaries.sort_by(|a, b| a.name.cmp(&b.name));

        // Assign row numbers after sorting (1-based)
        for (i, summary) in summaries.iter_mut().enumerate() {
            summary.row = i + 1;
        }

        Ok(summaries)
    }

    // ==================== Task Document Operations ====================

    /// Read task.md (user's original description - READ ONLY!)
    pub async fn read_task_md(&self, name: &str) -> K4000Result<String> {
        let path = self.task_md_path(name);
        if !path.exists() {
            return Err(K4000Error::TaskNotFound(name.to_string()));
        }
        let content = fs::read_to_string(&path).await?;
        Ok(content)
    }

    /// Check if task.md exists
    pub async fn task_md_exists(&self, name: &str) -> bool {
        self.task_md_path(name).exists()
    }

    /// Read plan.md
    pub async fn read_plan_md(&self, name: &str) -> K4000Result<String> {
        let path = self.plan_md_path(name);
        if !path.exists() {
            return Ok(String::new()); // Plan might not exist yet
        }
        let content = fs::read_to_string(&path).await?;
        Ok(content)
    }

    /// Write plan.md
    ///
    /// Automatically appends the K4000 workflow section if not already present.
    /// This ensures all projects using K4000 follow the standard implementation flow.
    pub async fn write_plan_md(&self, name: &str, content: &str) -> K4000Result<()> {
        self.ensure_task_folder(name).await?;
        let path = self.plan_md_path(name);

        // Check if content already contains the workflow section
        let final_content = if content.contains("## K4000 Workflow (MANDATORY)") {
            content.to_string()
        } else {
            format!("{}{}", content.trim_end(), K4000_WORKFLOW_SECTION)
        };

        fs::write(&path, final_content).await?;
        Ok(())
    }

    /// Read questions from questions.json
    pub async fn read_questions(&self, name: &str) -> K4000Result<Vec<Question>> {
        let path = self.questions_json_path(name);
        if !path.exists() {
            return Ok(Vec::new()); // Questions might not exist yet
        }
        let content = fs::read_to_string(&path).await?;
        let questions: Vec<Question> = serde_json::from_str(&content)?;
        Ok(questions)
    }

    /// Write questions to questions.json
    pub async fn write_questions(&self, name: &str, questions: &[Question]) -> K4000Result<()> {
        self.ensure_task_folder(name).await?;
        let path = self.questions_json_path(name);
        let content = serde_json::to_string_pretty(questions)?;
        fs::write(&path, content).await?;
        Ok(())
    }

    /// Read features from feature-list.json
    pub async fn read_features(&self, name: &str) -> K4000Result<Vec<Feature>> {
        let path = self.feature_list_json_path(name);
        if !path.exists() {
            return Ok(Vec::new()); // Features might not exist yet
        }
        let content = fs::read_to_string(&path).await?;
        let features: Vec<Feature> = serde_json::from_str(&content)?;
        Ok(features)
    }

    /// Write features to feature-list.json
    pub async fn write_features(&self, name: &str, features: &[Feature]) -> K4000Result<()> {
        self.ensure_task_folder(name).await?;
        let path = self.feature_list_json_path(name);
        let content = serde_json::to_string_pretty(features)?;
        fs::write(&path, content).await?;
        Ok(())
    }

    // ==================== Legacy Methods (for migration) ====================

    /// Get the old questions.md path - for migration only
    fn old_questions_md_path(&self, name: &str) -> PathBuf {
        self.task_folder(name).join("questions.md")
    }

    // ==================== Progress Log Operations ====================

    /// Append an entry to progress.txt
    ///
    /// Format: `[timestamp] [agent_name] message` (without feature)
    /// Format: `[timestamp] [agent_name] [feature_id] message` (with feature)
    pub async fn append_progress(
        &self,
        name: &str,
        agent_name: &str,
        message: &str,
    ) -> K4000Result<()> {
        self.append_progress_with_feature(name, agent_name, None, message)
            .await
    }

    /// Append an entry to progress.txt with optional feature ID
    ///
    /// Format: `[timestamp] [agent_name] message` (without feature)
    /// Format: `[timestamp] [agent_name] [feature_id] message` (with feature)
    pub async fn append_progress_with_feature(
        &self,
        name: &str,
        agent_name: &str,
        feature_id: Option<&str>,
        message: &str,
    ) -> K4000Result<()> {
        self.ensure_task_folder(name).await?;
        let path = self.progress_txt_path(name);

        let timestamp = chrono::Utc::now().format("%Y-%m-%dT%H:%M:%SZ");
        let entry = match feature_id {
            Some(fid) => format!("[{}] [{}] [{}] {}\n", timestamp, agent_name, fid, message),
            None => format!("[{}] [{}] {}\n", timestamp, agent_name, message),
        };

        // Append to file (create if doesn't exist)
        use tokio::io::AsyncWriteExt;
        let mut file = tokio::fs::OpenOptions::new()
            .create(true)
            .append(true)
            .open(&path)
            .await?;
        file.write_all(entry.as_bytes()).await?;

        Ok(())
    }

    /// Read progress.txt content
    pub async fn read_progress(&self, name: &str) -> K4000Result<String> {
        let path = self.progress_txt_path(name);
        if !path.exists() {
            return Ok(String::new()); // Progress log might not exist yet
        }
        let content = fs::read_to_string(&path).await?;
        Ok(content)
    }

    // ==================== Review Operations ====================

    /// Load a review session
    pub async fn load_review(
        &self,
        task_name: &str,
        review_id: &str,
    ) -> K4000Result<ReviewSession> {
        let path = self.review_path(task_name, review_id);
        if !path.exists() {
            return Err(K4000Error::ReviewNotFound(review_id.to_string()));
        }
        let content = fs::read_to_string(&path).await?;
        let review: ReviewSession = serde_json::from_str(&content)?;
        Ok(review)
    }

    /// Save a review session
    pub async fn save_review(&self, task_name: &str, review: &ReviewSession) -> K4000Result<()> {
        self.ensure_review_dir(task_name).await?;
        let path = self.review_path(task_name, &review.id);
        let content = serde_json::to_string_pretty(review)?;
        fs::write(&path, content).await?;
        Ok(())
    }

    /// List all reviews for a task
    pub async fn list_reviews(&self, task_name: &str) -> K4000Result<Vec<String>> {
        let review_dir = self.review_dir(task_name);
        if !review_dir.exists() {
            return Ok(vec![]);
        }

        let mut review_ids = Vec::new();
        let mut entries = fs::read_dir(&review_dir).await?;

        while let Some(entry) = entries.next_entry().await? {
            let file_name = entry.file_name().to_string_lossy().to_string();
            if file_name.starts_with("review-") && file_name.ends_with(".json") {
                // Extract the review ID from "review-{id}.json"
                let id = file_name
                    .strip_prefix("review-")
                    .and_then(|s| s.strip_suffix(".json"))
                    .map(|s| s.to_string());
                if let Some(id) = id {
                    review_ids.push(id);
                }
            }
        }

        // Sort by ID (which typically contains timestamp info)
        review_ids.sort();
        Ok(review_ids)
    }

    /// Get the latest review for a task
    pub async fn get_latest_review(&self, task_name: &str) -> K4000Result<Option<ReviewSession>> {
        let review_ids = self.list_reviews(task_name).await?;
        if let Some(latest_id) = review_ids.last() {
            Ok(Some(self.load_review(task_name, latest_id).await?))
        } else {
            Ok(None)
        }
    }

    /// Delete a review session
    pub async fn delete_review(&self, task_name: &str, review_id: &str) -> K4000Result<()> {
        let path = self.review_path(task_name, review_id);
        if path.exists() {
            fs::remove_file(&path).await?;
        }
        Ok(())
    }

    // ==================== Migration Helpers ====================

    /// Check if project has old features/ folder
    pub fn has_features_folder(&self) -> bool {
        self.project_path.join("features").exists()
    }

    /// Check if project has new tasks/ folder
    pub fn has_tasks_folder(&self) -> bool {
        self.tasks_dir().exists()
    }

    /// Check if a task needs migration from old structure
    ///
    /// Returns true if:
    /// - Old state.json exists in .state/task-name/
    /// - New state.json does NOT exist in task-name/
    pub fn needs_migration(&self, name: &str) -> bool {
        let old_state = self.old_task_state_dir(name).join("state.json");
        let new_state = self.task_state_path(name);
        old_state.exists() && !new_state.exists()
    }

    /// Migrate a single task from old structure to new structure
    ///
    /// This will:
    /// 1. Read old state.json (which has features, questions, name, project_path)
    /// 2. Write new slim state.json (only status, timestamps, review state)
    /// 3. Write features to feature-list.json (if not already there)
    /// 4. Write questions to questions.json (converting from state or questions.md)
    /// 5. Move review files from .state/task/ to task/.review/
    /// 6. Delete old .state/task/ directory
    /// 7. Try to delete .state/ if empty
    pub async fn migrate_task(&self, name: &str) -> K4000Result<()> {
        let old_state_path = self.old_task_state_dir(name).join("state.json");
        if !old_state_path.exists() {
            return Ok(()); // Nothing to migrate
        }

        // Read old state.json
        let old_content = fs::read_to_string(&old_state_path).await?;
        let old_state: serde_json::Value = serde_json::from_str(&old_content)?;

        // Extract fields for new slim state
        let status = old_state
            .get("status")
            .and_then(|v| v.as_str())
            .unwrap_or("draft");
        let created_at = old_state
            .get("created_at")
            .and_then(|v| v.as_str())
            .unwrap_or_else(|| "");
        let updated_at = old_state
            .get("updated_at")
            .and_then(|v| v.as_str())
            .unwrap_or_else(|| "");
        let current_review = old_state
            .get("current_review")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());
        let review_iterations = old_state
            .get("review_iterations")
            .and_then(|v| v.as_u64())
            .unwrap_or(0) as u32;
        let feature_approval_mode = old_state
            .get("feature_approval_mode")
            .and_then(|v| v.as_str())
            .map(|s| s.to_string());

        // Create new slim state
        let new_state = serde_json::json!({
            "status": status,
            "created_at": created_at,
            "updated_at": updated_at,
            "current_review": current_review,
            "review_iterations": review_iterations,
            "feature_approval_mode": feature_approval_mode,
        });

        // Ensure task folder exists
        self.ensure_task_folder(name).await?;

        // Write new state.json
        let new_state_path = self.task_state_path(name);
        let new_state_content = serde_json::to_string_pretty(&new_state)?;
        fs::write(&new_state_path, new_state_content).await?;

        // Migrate features (if old state has them and feature-list.json doesn't exist)
        let feature_list_path = self.feature_list_json_path(name);
        if !feature_list_path.exists() {
            if let Some(features) = old_state.get("features") {
                let features_content = serde_json::to_string_pretty(features)?;
                fs::write(&feature_list_path, features_content).await?;
            }
        }

        // Migrate questions to questions.json
        let questions_json_path = self.questions_json_path(name);
        if !questions_json_path.exists() {
            if let Some(questions) = old_state.get("questions") {
                let questions_content = serde_json::to_string_pretty(questions)?;
                fs::write(&questions_json_path, questions_content).await?;
            }
        }

        // Delete old questions.md if questions.json now exists
        let old_questions_md = self.old_questions_md_path(name);
        if questions_json_path.exists() && old_questions_md.exists() {
            let _ = fs::remove_file(&old_questions_md).await;
        }

        // Move review files from old location to new .review/ directory
        let old_task_state_dir = self.old_task_state_dir(name);
        if old_task_state_dir.exists() {
            let mut entries = fs::read_dir(&old_task_state_dir).await?;
            while let Some(entry) = entries.next_entry().await? {
                let file_name = entry.file_name().to_string_lossy().to_string();
                if file_name.starts_with("review-") && file_name.ends_with(".json") {
                    // Move to new location
                    self.ensure_review_dir(name).await?;
                    let old_path = entry.path();
                    let new_path = self.review_dir(name).join(&file_name);
                    fs::rename(&old_path, &new_path).await?;
                }
            }

            // Delete old task state directory
            let _ = fs::remove_dir_all(&old_task_state_dir).await;
        }

        // Try to delete .state/ directory if empty
        let old_state_dir = self.old_state_dir();
        if old_state_dir.exists() {
            // Check if empty
            let mut entries = fs::read_dir(&old_state_dir).await?;
            if entries.next_entry().await?.is_none() {
                let _ = fs::remove_dir(&old_state_dir).await;
            }
        }

        // Log migration
        self.append_progress(name, "k4000", "Migrated task to new state structure")
            .await?;

        Ok(())
    }
}

/// Sanitize a name to be safe for use in file paths
fn sanitize_name(name: &str) -> String {
    name.chars()
        .map(|c| {
            if c.is_alphanumeric() || c == '-' || c == '_' {
                c
            } else if c == ' ' {
                '-'
            } else {
                '_'
            }
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::{
        Question, QuestionCategory, ReviewCategory, ReviewFinding, Severity, TaskStatus,
    };

    #[test]
    fn test_sanitize_name() {
        assert_eq!(sanitize_name("my-task"), "my-task");
        assert_eq!(sanitize_name("my task"), "my-task");
        assert_eq!(sanitize_name("my/task"), "my_task");
        assert_eq!(sanitize_name("my.task"), "my_task");
        assert_eq!(sanitize_name("MyTask123"), "MyTask123");
    }

    #[test]
    fn test_paths() {
        let manager = StateManager::new("/project");
        assert_eq!(manager.tasks_dir(), PathBuf::from("/project/tasks"));
        assert_eq!(
            manager.old_state_dir(),
            PathBuf::from("/project/tasks/.state")
        );
        assert_eq!(
            manager.old_task_state_dir("test"),
            PathBuf::from("/project/tasks/.state/test")
        );
        assert_eq!(
            manager.task_folder("test"),
            PathBuf::from("/project/tasks/test")
        );
        assert_eq!(
            manager.task_state_path("test"),
            PathBuf::from("/project/tasks/test/state.json")
        );
        assert_eq!(
            manager.task_md_path("test"),
            PathBuf::from("/project/tasks/test/task.md")
        );
        assert_eq!(
            manager.plan_md_path("test"),
            PathBuf::from("/project/tasks/test/plan.md")
        );
        assert_eq!(
            manager.questions_json_path("test"),
            PathBuf::from("/project/tasks/test/questions.json")
        );
        assert_eq!(
            manager.feature_list_json_path("test"),
            PathBuf::from("/project/tasks/test/feature-list.json")
        );
        assert_eq!(
            manager.progress_txt_path("test"),
            PathBuf::from("/project/tasks/test/progress.txt")
        );
        assert_eq!(
            manager.review_dir("test"),
            PathBuf::from("/project/tasks/test/.review")
        );
        assert_eq!(
            manager.review_path("test", "abc123"),
            PathBuf::from("/project/tasks/test/.review/review-abc123.json")
        );
    }

    #[tokio::test]
    async fn test_task_state_crud() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create a task state
        let mut state = TaskState::new();
        state.status = TaskStatus::Analyzing;

        // Save it
        manager.save_task_state("test-task", &state).await.unwrap();

        // Load it back
        let loaded = manager.load_task_state("test-task").await.unwrap();
        assert_eq!(loaded.status, TaskStatus::Analyzing);

        // Check exists
        assert!(manager.task_exists("test-task").await);
        assert!(!manager.task_exists("nonexistent").await);

        // List tasks
        let tasks = manager.list_tasks().await.unwrap();
        assert_eq!(tasks.len(), 1);
        assert_eq!(tasks[0].name, "test-task");

        // Delete it
        manager.delete_task_state("test-task").await.unwrap();
        assert!(!manager.task_exists("test-task").await);
    }

    #[tokio::test]
    async fn test_task_documents() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create task folder and task.md manually (simulating user creation)
        let task_folder = manager.task_folder("test-task");
        fs::create_dir_all(&task_folder).await.unwrap();
        fs::write(
            task_folder.join("task.md"),
            "# My Task\n\nUser description.",
        )
        .await
        .unwrap();

        // Read task.md
        let task_content = manager.read_task_md("test-task").await.unwrap();
        assert!(task_content.contains("User description"));

        // Write plan.md
        let plan_content = "# Implementation Plan\n\n## Tasks\n- [ ] T1: Do something";
        manager
            .write_plan_md("test-task", plan_content)
            .await
            .unwrap();

        // Read plan.md back - should include appended workflow section
        let loaded_plan = manager.read_plan_md("test-task").await.unwrap();
        assert!(loaded_plan.starts_with(plan_content.trim_end()));
        assert!(loaded_plan.contains("## K4000 Workflow (MANDATORY)"));
        assert!(loaded_plan.contains("Feature-by-Feature Implementation"));
        assert!(loaded_plan.contains("Code Review (AUTOMATIC"));

        // Writing again with workflow section already present should not duplicate it
        manager
            .write_plan_md("test-task", &loaded_plan)
            .await
            .unwrap();
        let reloaded_plan = manager.read_plan_md("test-task").await.unwrap();
        assert_eq!(
            reloaded_plan
                .matches("## K4000 Workflow (MANDATORY)")
                .count(),
            1,
            "Workflow section should not be duplicated"
        );

        // Write questions.json
        let questions = vec![Question::new(
            "q1",
            "What is the expected behavior?",
            QuestionCategory::Clarification,
        )];
        manager
            .write_questions("test-task", &questions)
            .await
            .unwrap();

        // Read questions.json back
        let loaded_questions = manager.read_questions("test-task").await.unwrap();
        assert_eq!(loaded_questions.len(), 1);
        assert_eq!(loaded_questions[0].id, "q1");
    }

    #[tokio::test]
    async fn test_review_crud() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create a task first
        let state = TaskState::new();
        manager.save_task_state("test-task", &state).await.unwrap();

        // Create a review session
        let mut review = ReviewSession::new("review-001", "test-task", 1);
        review.files_reviewed = vec!["src/lib.rs".to_string()];
        review.findings.push(
            ReviewFinding::new(
                "f1",
                ReviewCategory::Bugs,
                "src/lib.rs",
                Severity::High,
                "Potential null pointer dereference",
            )
            .with_line(42)
            .with_suggestion("Add null check before accessing"),
        );

        // Save it
        manager.save_review("test-task", &review).await.unwrap();

        // Load it back
        let loaded = manager
            .load_review("test-task", "review-001")
            .await
            .unwrap();
        assert_eq!(loaded.id, "review-001");
        assert_eq!(loaded.task_name, "test-task");
        assert_eq!(loaded.iteration, 1);
        assert_eq!(loaded.findings.len(), 1);
        assert_eq!(loaded.findings[0].id, "f1");
        assert_eq!(loaded.findings[0].line, Some(42));

        // List reviews
        let reviews = manager.list_reviews("test-task").await.unwrap();
        assert_eq!(reviews.len(), 1);
        assert_eq!(reviews[0], "review-001");

        // Get latest review
        let latest = manager.get_latest_review("test-task").await.unwrap();
        assert!(latest.is_some());
        assert_eq!(latest.unwrap().id, "review-001");

        // Delete review
        manager
            .delete_review("test-task", "review-001")
            .await
            .unwrap();
        let reviews = manager.list_reviews("test-task").await.unwrap();
        assert!(reviews.is_empty());
    }

    #[tokio::test]
    async fn test_task_not_found() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        let result = manager.load_task_state("nonexistent").await;
        assert!(matches!(result, Err(K4000Error::TaskNotFound(_))));
    }

    #[tokio::test]
    async fn test_review_not_found() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create task first
        let state = TaskState::new();
        manager.save_task_state("test-task", &state).await.unwrap();

        let result = manager.load_review("test-task", "nonexistent").await;
        assert!(matches!(result, Err(K4000Error::ReviewNotFound(_))));
    }

    #[tokio::test]
    async fn test_progress_log() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create task folder
        manager.ensure_task_folder("test-task").await.unwrap();

        // Initially empty
        let progress = manager.read_progress("test-task").await.unwrap();
        assert!(progress.is_empty());

        // Append entries
        manager
            .append_progress("test-task", "k4000", "Task created")
            .await
            .unwrap();
        manager
            .append_progress("test-task", "claude", "Starting implementation")
            .await
            .unwrap();

        // Read back
        let progress = manager.read_progress("test-task").await.unwrap();
        assert!(progress.contains("[k4000] Task created"));
        assert!(progress.contains("[claude] Starting implementation"));

        // Check format (should have timestamps)
        let lines: Vec<&str> = progress.lines().collect();
        assert_eq!(lines.len(), 2);
        assert!(lines[0].starts_with('['));
        assert!(lines[0].contains("] [k4000] Task created"));
    }

    #[test]
    fn test_has_folders() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Neither folder exists initially
        assert!(!manager.has_features_folder());
        assert!(!manager.has_tasks_folder());

        // Create features folder
        std::fs::create_dir(temp_dir.path().join("features")).unwrap();
        assert!(manager.has_features_folder());
        assert!(!manager.has_tasks_folder());

        // Create tasks folder
        std::fs::create_dir(temp_dir.path().join("tasks")).unwrap();
        assert!(manager.has_features_folder());
        assert!(manager.has_tasks_folder());
    }

    #[tokio::test]
    async fn test_project_config() {
        use crate::types::FeatureApprovalMode;

        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Initially no config exists - should return defaults
        assert!(!manager.has_project_config());
        let config = manager.load_project_config().await.unwrap();
        assert_eq!(config.feature_approval_mode, FeatureApprovalMode::Manual);
        assert_eq!(config.acceptance_criteria_max_iterations, 5);

        // Create config with auto mode
        let custom_config = ProjectConfig {
            feature_approval_mode: FeatureApprovalMode::Auto,
            acceptance_criteria_max_iterations: 7,
            auto_commit_on_done: true,
        };
        manager.save_project_config(&custom_config).await.unwrap();

        // Now config should exist
        assert!(manager.has_project_config());

        // Load it back
        let loaded = manager.load_project_config().await.unwrap();
        assert_eq!(loaded.feature_approval_mode, FeatureApprovalMode::Auto);
        assert_eq!(loaded.acceptance_criteria_max_iterations, 7);

        // Verify file path is correct
        let config_path = manager.config_path();
        assert!(config_path.exists());
        assert!(config_path.ends_with("tasks/.config/config.toml"));
    }

    #[test]
    fn test_config_paths() {
        let manager = StateManager::new("/project");
        assert_eq!(
            manager.config_dir(),
            PathBuf::from("/project/tasks/.config")
        );
        assert_eq!(
            manager.config_path(),
            PathBuf::from("/project/tasks/.config/config.toml")
        );
    }

    #[tokio::test]
    async fn test_ensure_config_initialized_creates_new() {
        use crate::types::FeatureApprovalMode;

        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Config doesn't exist yet
        assert!(!manager.has_project_config());

        // Call ensure_config_initialized
        let (config, message) = manager.ensure_config_initialized().await.unwrap();

        // Should return defaults
        assert_eq!(config.feature_approval_mode, FeatureApprovalMode::Manual);
        assert_eq!(config.acceptance_criteria_max_iterations, 5);

        // Should have a creation message
        assert!(message.is_some());
        assert!(message.unwrap().contains("Created default config"));

        // Config file should now exist
        assert!(manager.has_project_config());

        // File should contain comments
        let content = std::fs::read_to_string(manager.config_path()).unwrap();
        assert!(content.contains("# K4000 Project Configuration"));
        assert!(content.contains("# Feature approval mode"));
        assert!(content.contains("feature_approval_mode = \"manual\""));
        assert!(content.contains("# Maximum number of verification loops"));
        assert!(content.contains("acceptance_criteria_max_iterations = 5"));
    }

    #[tokio::test]
    async fn test_ensure_config_initialized_preserves_existing() {
        use crate::types::FeatureApprovalMode;

        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create config with auto mode
        manager.ensure_config_dir().await.unwrap();
        std::fs::write(
            manager.config_path(),
            "# User comment\nfeature_approval_mode = \"auto\"\n",
        )
        .unwrap();

        // Call ensure_config_initialized
        let (config, message) = manager.ensure_config_initialized().await.unwrap();

        // Should preserve the "auto" setting
        assert_eq!(config.feature_approval_mode, FeatureApprovalMode::Auto);
        assert_eq!(config.acceptance_criteria_max_iterations, 5);

        // Should have an update message (missing acceptance criteria setting)
        assert!(message.is_some());
        assert!(message.unwrap().contains("Updated config"));

        // File content should include missing setting
        let content = std::fs::read_to_string(manager.config_path()).unwrap();
        assert!(content.contains("# User comment"));
        assert!(content.contains("feature_approval_mode = \"auto\""));
        assert!(content.contains("acceptance_criteria_max_iterations = 5"));
    }

    #[tokio::test]
    async fn test_ensure_config_initialized_adds_missing_settings() {
        use crate::types::FeatureApprovalMode;

        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // Create empty config file
        manager.ensure_config_dir().await.unwrap();
        std::fs::write(manager.config_path(), "# Empty config\n").unwrap();

        // Call ensure_config_initialized
        let (config, message) = manager.ensure_config_initialized().await.unwrap();

        // Should use default for missing settings
        assert_eq!(config.feature_approval_mode, FeatureApprovalMode::Manual);
        assert_eq!(config.acceptance_criteria_max_iterations, 5);

        // Should have an update message
        assert!(message.is_some());
        assert!(message.unwrap().contains("Updated config"));

        // File should now contain the missing settings
        let content = std::fs::read_to_string(manager.config_path()).unwrap();
        assert!(content.contains("# Empty config")); // Original comment preserved
        assert!(content.contains("feature_approval_mode = \"manual\""));
        assert!(content.contains("acceptance_criteria_max_iterations = 5"));
    }

    #[tokio::test]
    async fn test_ensure_config_initialized_idempotent() {
        let temp_dir = tempfile::tempdir().unwrap();
        let manager = StateManager::new(temp_dir.path());

        // First call creates config
        let (_, msg1) = manager.ensure_config_initialized().await.unwrap();
        assert!(msg1.is_some());

        // Second call should be a no-op
        let (_, msg2) = manager.ensure_config_initialized().await.unwrap();
        assert!(msg2.is_none());

        // Third call should also be a no-op
        let (_, msg3) = manager.ensure_config_initialized().await.unwrap();
        assert!(msg3.is_none());
    }
}
