use anyhow::{bail, Result};
use chrono::Utc;
use clap::Parser;
use colored::Colorize;
use dialoguer::{Confirm, Input};
use std::path::PathBuf;

use crate::config::{Language, manager::ConfigManager};
use crate::generator::ContentGenerator;
use crate::git::find_repo;
use crate::git::{changelog::*, CommitInfo};
use crate::i18n::{Messages, translate_changelog_category};

/// Generate changelog
#[derive(Parser)]
#[command(disable_version_flag = true, disable_help_flag = false)]
pub struct ChangelogCommand {
    /// Output file path
    #[arg(short, long)]
    output: Option<PathBuf>,

    /// Version to generate changelog for
    #[arg(long)]
    version: Option<String>,

    /// Generate from specific tag
    #[arg(short, long)]
    from: Option<String>,

    /// Generate to specific ref
    #[arg(short, long, default_value = "HEAD")]
    to: String,

    /// Initialize new changelog file
    #[arg(short, long)]
    init: bool,

    /// Generate with AI
    #[arg(short, long)]
    generate: bool,

    /// Include commit hashes
    #[arg(long)]
    include_hashes: bool,

    /// Include authors
    #[arg(long)]
    include_authors: bool,

    /// Format (keep-a-changelog, github-releases)
    #[arg(long)]
    format: Option<String>,

    /// Dry run (output to stdout)
    #[arg(long)]
    dry_run: bool,

    /// Skip interactive prompts
    #[arg(short = 'y', long)]
    yes: bool,
}

impl ChangelogCommand {
    pub async fn execute(&self, config_path: Option<PathBuf>) -> Result<()> {
        let repo = find_repo(std::env::current_dir()?.as_path())?;
        let manager = if let Some(ref path) = config_path {
            ConfigManager::with_path(path)?
        } else {
            ConfigManager::new()?
        };
        let config = manager.config();
        let language = manager.get_language().unwrap_or(Language::English);
        let messages = Messages::new(language);

        // Initialize changelog if requested
        if self.init {
            let path = self.output.as_ref()
                .map(|p| p.clone())
                .unwrap_or_else(|| PathBuf::from(&config.changelog.path));
            
            init_changelog(&path)?;
            println!("{}", messages.initialized_changelog(&format!("{:?}", path)));
            return Ok(());
        }

        // Determine output path
        let output_path = self.output.as_ref()
            .map(|p| p.clone())
            .unwrap_or_else(|| PathBuf::from(&config.changelog.path));

        // Determine format
        let format = match self.format.as_deref() {
            Some("github") | Some("github-releases") => ChangelogFormat::GitHubReleases,
            Some("keep") | Some("keep-a-changelog") => ChangelogFormat::KeepAChangelog,
            Some("custom") => ChangelogFormat::Custom,
            None => ChangelogFormat::KeepAChangelog,
            Some(f) => bail!("Unknown format: {}. Use: keep-a-changelog, github-releases", f),
        };

        // Get version
        let version = if let Some(ref v) = self.version {
            v.clone()
        } else if !self.yes {
            Input::new()
                .with_prompt(messages.version())
                .default(messages.unreleased().to_string())
                .interact_text()?
        } else {
            messages.unreleased().to_string()
        };

        // Get commits
        println!("{}", messages.fetching_commits());
        let commits = generate_from_history(&repo, self.from.as_deref(), Some(&self.to))?;
        
        if commits.is_empty() {
            bail!("{}", messages.no_commits_found());
        }
        
        println!("{}", messages.found_commits(commits.len()));

        // Generate changelog
        let changelog = if self.generate || (config.changelog.auto_generate && !self.yes) {
            self.generate_with_ai(&version, &commits, &messages).await?
        } else {
            self.generate_with_template(format, &version, &commits, language)?
        };

        // Output or write
        if self.dry_run {
            println!("\n{}", "─".repeat(60));
            println!("{}", changelog);
            println!("{}", "─".repeat(60));
            return Ok(());
        }

        // Preview
        if !self.yes {
            println!("\n{}", "─".repeat(60));
            println!("{}", messages.changelog_preview().bold());
            println!("{}", "─".repeat(60));
            // Show first 20 lines
            let preview: String = changelog.lines().take(20).collect::<Vec<_>>().join("\n");
            println!("{}", preview);
            if changelog.lines().count() > 20 {
                println!("\n... ({} more lines)", changelog.lines().count() - 20);
            }
            println!("{}", "─".repeat(60));

            let confirm = Confirm::new()
                .with_prompt(&messages.write_to_file(&format!("{:?}", output_path)))
                .default(true)
                .interact()?;

            if !confirm {
                println!("{}", messages.cancelled().yellow());
                return Ok(());
            }
        }

        // Write to file (always prepend to preserve history)
        if output_path.exists() {
            let existing = std::fs::read_to_string(&output_path)?;
            let new_content = if existing.is_empty() {
                format!("{}{}", CHANGELOG_HEADER, changelog)
            } else if existing.starts_with(CHANGELOG_HEADER) {
                format!("{}{}", CHANGELOG_HEADER, changelog)
            } else if existing.starts_with("# Changelog") {
                let lines: Vec<&str> = existing.lines().collect();
                let mut header_end = 0;
                
                for (i, line) in lines.iter().enumerate() {
                    if i == 0 && line.starts_with('#') {
                        header_end = i + 1;
                    } else if line.trim().is_empty() {
                        header_end = i + 1;
                    } else {
                        break;
                    }
                }
                
                let header = lines[..header_end].join("\n");
                let rest = lines[header_end..].join("\n");
                
                format!("{}\n{}\n{}", header, changelog, rest)
            } else {
                format!("{}{}", CHANGELOG_HEADER, changelog)
            };
            std::fs::write(&output_path, new_content)?;
        } else {
            let content = format!("{}{}", CHANGELOG_HEADER, changelog);
            std::fs::write(&output_path, content)?;
        }

        println!("{} {:?}", messages.changelog_written(), output_path);

        Ok(())
    }

    async fn generate_with_ai(
        &self,
        version: &str,
        commits: &[CommitInfo],
        messages: &Messages,
    ) -> Result<String> {
        let manager = ConfigManager::new()?;
        let language = manager.get_language().unwrap_or(Language::English);

        println!("{}", messages.ai_generating_changelog());

        let generator = ContentGenerator::new(&manager).await?;
        generator.generate_changelog_entry(version, commits, language).await
    }

    fn generate_with_template(
        &self,
        format: ChangelogFormat,
        version: &str,
        commits: &[CommitInfo],
        language: Language,
    ) -> Result<String> {
        let manager = ConfigManager::new()?;
        
        let generator = ChangelogGenerator::new()
            .format(format)
            .include_hashes(self.include_hashes)
            .include_authors(self.include_authors);

        let changelog = generator.generate(version, Utc::now(), commits)?;
        
        // Translate changelog categories if configured
        if !manager.keep_changelog_types_english() {
            Ok(self.translate_changelog_categories(&changelog, language))
        } else {
            Ok(changelog)
        }
    }
    
    fn translate_changelog_categories(&self, changelog: &str, language: Language) -> String {
        let translated = changelog
            .lines()
            .map(|line| {
                if line.starts_with("## ") || line.starts_with("### ") {
                    let category = line.trim_start_matches("## ").trim_start_matches("### ");
                    let translated_category = translate_changelog_category(category, language, false);
                    if line.starts_with("## ") {
                        format!("## {}", translated_category)
                    } else {
                        format!("### {}", translated_category)
                    }
                } else {
                    line.to_string()
                }
            })
            .collect::<Vec<_>>()
            .join("\n");
        translated
    }
}
