//! Export handler for project data in various formats
//!
//! Supports JSON, CSV, TOML, and other formats.

#![allow(dead_code)]

use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use std::path::Path;
use syntaxis_core::persistence::{Database, SqliteDatabase};
use syntaxis_core::{ExportFormat, ProjectConfig};

/// Checklist item export structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChecklistItemExport {
    /// Task name
    pub name: String,
    /// Task description
    pub description: String,
    /// Task type (feature, bug, improvement, etc.)
    pub task_type: String,
    /// Task priority
    pub priority: String,
    /// Due date in YYYY-MM-DD format
    pub due_date: Option<String>,
    /// Time estimation
    pub estimation: Option<String>,
    /// Task dependencies
    pub dependencies: Vec<String>,
    /// Task notes
    pub notes: Option<String>,
    /// Completion status
    pub completed: bool,
    /// Completion date
    pub completed_at: Option<String>,
    /// Phase where the task belongs
    pub phase: String,
}

/// Project export data structure
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectExport {
    /// Project ID
    pub id: String,
    /// Project name
    pub name: String,
    /// Project type
    pub project_type: String,
    /// Description
    pub description: String,
    /// Current phase
    pub current_phase: String,
    /// Version
    pub version: String,
    /// Created date
    pub created_at: String,
    /// Updated date
    pub updated_at: String,
    /// Tags
    #[serde(default)]
    pub tags: Vec<String>,
    /// Status
    #[serde(default)]
    pub status: String,
    /// Metadata
    #[serde(default)]
    pub metadata: serde_json::Value,
    /// Checklist items
    #[serde(default)]
    pub checklist_items: Vec<ChecklistItemExport>,
}

/// Batch export data
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchExport {
    /// Export timestamp
    pub exported_at: String,
    /// Number of projects
    pub project_count: usize,
    /// Exported projects
    pub projects: Vec<ProjectExport>,
}

/// Export configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportConfig {
    /// Export format
    pub format: String,
    /// Output file path
    pub output_file: String,
    /// Include metadata
    #[serde(default = "default_include_metadata")]
    pub include_metadata: bool,
    /// Include tags
    #[serde(default = "default_include_tags")]
    pub include_tags: bool,
    /// Pretty print (for JSON/TOML/YAML)
    #[serde(default = "default_pretty")]
    pub pretty: bool,
    /// Filter by phase (optional)
    #[serde(default)]
    pub filter_phase: Option<String>,
    /// Filter by type (optional)
    #[serde(default)]
    pub filter_type: Option<String>,
}

fn default_include_metadata() -> bool {
    true
}

fn default_include_tags() -> bool {
    true
}

fn default_pretty() -> bool {
    true
}

/// Export handler
pub struct ExportHandler;

impl ExportHandler {
    /// Substitute {timestamp} placeholder in path with current timestamp
    pub fn substitute_timestamp(path: &str) -> String {
        let now = chrono::Local::now();
        let timestamp = now.format("%Y%m%d-%H%M%S").to_string();
        path.replace("{timestamp}", &timestamp)
    }

    /// Execute export of a single project using database and config
    pub async fn execute_export(
        config_path: &Path,
        format: Option<ExportFormat>,
        output_path: Option<&str>,
        _project_id: Option<&str>,
    ) -> Result<()> {
        // Load configuration
        let config = ProjectConfig::load_from_file(config_path)?;

        // Determine output path with timestamp substitution
        let output = if let Some(out) = output_path {
            Self::substitute_timestamp(out)
        } else if let Some(out) = &config.export.output_path {
            Self::substitute_timestamp(out)
        } else {
            return Err(anyhow!("No output path specified in config or arguments"));
        };

        // Determine format
        let export_format = format.unwrap_or(config.export.format);

        // Create output directory if needed
        if let Some(parent) = Path::new(&output).parent() {
            if !parent.as_os_str().is_empty() && parent.to_string_lossy() != "." {
                std::fs::create_dir_all(parent)?;
            }
        }

        // Setup database path
        let project_root = config_path
            .parent()
            .and_then(|p| p.parent())
            .unwrap_or_else(|| Path::new("."));
        let db_path = project_root.join(&config.database_path);
        let db_path_str = db_path.to_string_lossy().to_string();

        // Load database
        let db = SqliteDatabase::new(&db_path_str).await?;

        // Generate project ID based on name
        let project_id = config.project.name.to_lowercase().replace(" ", "_");

        // Build export data
        let mut project_data = ProjectExport {
            id: project_id.clone(),
            name: config.project.name.clone(),
            project_type: config.project_type.to_string(),
            description: config.project.description.clone(),
            current_phase: format!("{:?}", config.phase),
            version: config.project.version.clone(),
            created_at: config.project.created.to_rfc3339(),
            updated_at: config.project.last_updated.to_rfc3339(),
            tags: config.project.categories.clone(),
            status: "active".to_string(),
            metadata: if config.export.include_metadata {
                serde_json::json!({
                    "type": config.project_type.to_string(),
                    "language": config.primary_language,
                    "repository": config.project.repository,
                    "license": config.project.license,
                    "export_date": chrono::Local::now().to_rfc3339(),
                })
            } else {
                serde_json::json!({})
            },
            checklist_items: Vec::new(),
        };

        // Load checklist items if they exist
        if let Ok(items) = db.get_checklist_items(&project_id).await {
            project_data.checklist_items = items
                .into_iter()
                .map(|item| ChecklistItemExport {
                    name: item.task_name.unwrap_or_else(|| item.task_id.clone()),
                    description: item.description,
                    task_type: item.task_type,
                    priority: item.task_priority,
                    due_date: item.task_due,
                    estimation: item.task_estimation,
                    dependencies: if item.task_deps.is_empty() {
                        Vec::new()
                    } else {
                        item.task_deps.split(',').map(|s| s.to_string()).collect()
                    },
                    notes: item.task_note,
                    completed: item.completed,
                    completed_at: item.completed_at,
                    phase: item.phase,
                })
                .collect();
        }

        // Serialize based on format
        let content = Self::export_project(&project_data, export_format)?;

        // Write to file
        Self::export_to_file(&content, &output)?;

        println!("✓ Project exported successfully to: {}", output);
        println!("  Format: {:?}", export_format);
        println!("  Items: {}", project_data.checklist_items.len());

        Ok(())
    }

    /// Export single project
    pub fn export_project(project: &ProjectExport, format: ExportFormat) -> Result<String> {
        match format {
            ExportFormat::Json => Ok(serde_json::to_string_pretty(project)?),
            ExportFormat::Csv => Self::project_to_csv(project),
            ExportFormat::Toml => Ok(toml::to_string_pretty(project)?),
            ExportFormat::Yaml => {
                // YAML export would require yaml crate
                Ok(format!(
                    "id: {}\nname: {}\ntype: {}\nphase: {}\nversion: {}",
                    project.id,
                    project.name,
                    project.project_type,
                    project.current_phase,
                    project.version
                ))
            }
            ExportFormat::Markdown => Self::project_to_markdown(project),
        }
    }

    /// Export batch of projects
    pub fn export_batch(projects: &[ProjectExport], format: ExportFormat) -> Result<String> {
        let batch = BatchExport {
            exported_at: chrono::Local::now().to_rfc3339(),
            project_count: projects.len(),
            projects: projects.to_vec(),
        };

        match format {
            ExportFormat::Json => Ok(serde_json::to_string_pretty(&batch)?),
            ExportFormat::Csv => Self::batch_to_csv(&batch),
            ExportFormat::Toml => Ok(toml::to_string_pretty(&batch)?),
            ExportFormat::Yaml => Ok(format!(
                "exported_at: {}\nproject_count: {}\nprojects:\n{}",
                batch.exported_at,
                batch.project_count,
                batch
                    .projects
                    .iter()
                    .map(|p| format!("  - {}", p.name))
                    .collect::<Vec<_>>()
                    .join("\n")
            )),
            ExportFormat::Markdown => Self::batch_to_markdown(&batch),
        }
    }

    /// Convert project to CSV format
    fn project_to_csv(project: &ProjectExport) -> Result<String> {
        Ok(format!(
            "ID,Name,Type,Phase,Version,Status\n{},{},{},{},{},{}",
            project.id,
            project.name,
            project.project_type,
            project.current_phase,
            project.version,
            project.status
        ))
    }

    /// Convert batch to CSV format
    fn batch_to_csv(batch: &BatchExport) -> Result<String> {
        let mut csv = "ID,Name,Type,Phase,Version,Status\n".to_string();

        for project in &batch.projects {
            csv.push_str(&format!(
                "{},{},{},{},{},{}\n",
                project.id,
                project.name,
                project.project_type,
                project.current_phase,
                project.version,
                project.status
            ));
        }

        Ok(csv)
    }

    /// Convert project to Markdown format
    fn project_to_markdown(project: &ProjectExport) -> Result<String> {
        Ok(format!(
            "# {}\n\n## Details\n\n- **ID**: {}\n- **Type**: {}\n- **Phase**: {}\n- **Version**: {}\n- **Status**: {}\n- **Created**: {}\n- **Updated**: {}\n\n## Description\n\n{}",
            project.name,
            project.id,
            project.project_type,
            project.current_phase,
            project.version,
            project.status,
            project.created_at,
            project.updated_at,
            project.description
        ))
    }

    /// Convert batch to Markdown format
    fn batch_to_markdown(batch: &BatchExport) -> Result<String> {
        let mut md = format!(
            "# Project Export\n\nExported: {}\nTotal Projects: {}\n\n",
            batch.exported_at, batch.project_count
        );

        for project in &batch.projects {
            md.push_str(&format!(
                "## {}\n- Phase: {}\n- Type: {}\n- Version: {}\n\n",
                project.name, project.current_phase, project.project_type, project.version
            ));
        }

        Ok(md)
    }

    /// Export to file
    pub fn export_to_file(content: &str, path: impl AsRef<Path>) -> Result<()> {
        std::fs::write(path, content)?;
        Ok(())
    }

    /// Load export configuration
    pub fn load_config(path: impl AsRef<Path>) -> Result<ExportConfig> {
        let content = std::fs::read_to_string(path)?;
        let config = toml::from_str(&content)?;
        Ok(config)
    }
}

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

    fn create_test_project() -> ProjectExport {
        ProjectExport {
            id: "test-1".to_string(),
            name: "Test Project".to_string(),
            project_type: "web".to_string(),
            description: "A test project".to_string(),
            current_phase: "design".to_string(),
            version: "1.0.0".to_string(),
            created_at: "2024-01-01T00:00:00Z".to_string(),
            updated_at: "2024-01-02T00:00:00Z".to_string(),
            tags: vec!["test".to_string()],
            status: "active".to_string(),
            metadata: serde_json::json!({}),
            checklist_items: Vec::new(),
        }
    }

    #[test]
    fn test_export_format_from_str() {
        assert_eq!(ExportFormat::from_str("json").unwrap(), ExportFormat::Json);
        assert_eq!(ExportFormat::from_str("csv").unwrap(), ExportFormat::Csv);
        assert_eq!(ExportFormat::from_str("toml").unwrap(), ExportFormat::Toml);
        assert!(ExportFormat::from_str("invalid").is_err());
    }

    #[test]
    fn test_export_format_extension() {
        assert_eq!(ExportFormat::Json.extension(), "json");
        assert_eq!(ExportFormat::Csv.extension(), "csv");
        assert_eq!(ExportFormat::Toml.extension(), "toml");
    }

    #[test]
    fn test_export_project_json() {
        let project = create_test_project();
        let result = ExportHandler::export_project(&project, ExportFormat::Json);
        assert!(result.is_ok());
        assert!(result.unwrap().contains("Test Project"));
    }

    #[test]
    fn test_export_project_csv() {
        let project = create_test_project();
        let result = ExportHandler::export_project(&project, ExportFormat::Csv);
        assert!(result.is_ok());
        let csv = result.unwrap();
        assert!(csv.contains("test-1"));
        assert!(csv.contains("Test Project"));
    }

    #[test]
    fn test_export_project_markdown() {
        let project = create_test_project();
        let result = ExportHandler::export_project(&project, ExportFormat::Markdown);
        assert!(result.is_ok());
        let md = result.unwrap();
        assert!(md.contains("# Test Project"));
        assert!(md.contains("design"));
    }

    #[test]
    fn test_batch_export() {
        let projects = vec![create_test_project(), create_test_project()];
        let result = ExportHandler::export_batch(&projects, ExportFormat::Json);
        assert!(result.is_ok());
        let json = result.unwrap();
        assert!(json.contains("project_count"));
    }

    #[test]
    fn test_batch_export_csv() {
        let projects = vec![create_test_project(), create_test_project()];
        let result = ExportHandler::export_batch(&projects, ExportFormat::Csv);
        assert!(result.is_ok());
        let csv = result.unwrap();
        assert!(csv.contains("ID,Name,Type"));
    }

    #[test]
    fn test_export_config_defaults() {
        let config = ExportConfig {
            format: "json".to_string(),
            output_file: "export.json".to_string(),
            include_metadata: false,
            include_tags: false,
            pretty: false,
            filter_phase: None,
            filter_type: None,
        };

        assert!(!config.include_metadata);
        assert!(!config.pretty);
    }

    #[test]
    fn test_substitute_timestamp() {
        let path = ".project/exports/project-{timestamp}.json";
        let result = ExportHandler::substitute_timestamp(path);
        assert!(!result.contains("{timestamp}"));
        assert!(result.starts_with(".project/exports/project-"));
        assert!(result.ends_with(".json"));
        // Verify timestamp format (YYYYMMDD-HHMMSS)
        assert!(result.contains("-20")); // Year starts with 20
    }

    #[test]
    fn test_substitute_timestamp_no_placeholder() {
        let path = ".project/exports/project.json";
        let result = ExportHandler::substitute_timestamp(path);
        assert_eq!(result, path);
    }

    #[test]
    fn test_checklist_item_export_serialization() {
        let item = ChecklistItemExport {
            name: "Test Task".to_string(),
            description: "Test Description".to_string(),
            task_type: "feature".to_string(),
            priority: "high".to_string(),
            due_date: Some("2024-12-31".to_string()),
            estimation: Some("5h".to_string()),
            dependencies: vec!["task-1".to_string()],
            notes: Some("Important".to_string()),
            completed: false,
            completed_at: None,
            phase: "development".to_string(),
        };

        let json = serde_json::to_string(&item).unwrap();
        assert!(json.contains("Test Task"));
        assert!(json.contains("feature"));
        assert!(json.contains("2024-12-31"));
    }

    #[test]
    fn test_project_export_with_checklist() {
        let mut project = create_test_project();
        project.checklist_items = vec![ChecklistItemExport {
            name: "Task 1".to_string(),
            description: "First task".to_string(),
            task_type: "feature".to_string(),
            priority: "high".to_string(),
            due_date: None,
            estimation: None,
            dependencies: vec![],
            notes: None,
            completed: false,
            completed_at: None,
            phase: "development".to_string(),
        }];

        assert_eq!(project.checklist_items.len(), 1);
        assert_eq!(project.checklist_items[0].task_type, "feature");
    }
}
