use anyhow::{bail, Result};
use clap::Parser;
use colored::Colorize;
use dialoguer::{Confirm, Input, Select};
use semver::Version;

use crate::config::manager::ConfigManager;
use crate::git::{find_repo, GitRepo};
use crate::generator::ContentGenerator;
use crate::git::tag::{
    bump_version, get_latest_version, suggest_version_bump, TagBuilder, VersionBump,
};

/// Generate and create Git tags
#[derive(Parser)]
pub struct TagCommand {
    /// Tag name
    #[arg(short, long)]
    name: Option<String>,

    /// Semantic version bump
    #[arg(short, long, value_name = "TYPE")]
    bump: Option<String>,

    /// Tag message
    #[arg(short, long)]
    message: Option<String>,

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

    /// Sign the tag
    #[arg(short = 'S', long)]
    sign: bool,

    /// Create lightweight tag (no annotation)
    #[arg(short, long)]
    lightweight: bool,

    /// Force overwrite existing tag
    #[arg(short, long)]
    force: bool,

    /// Push tag to remote after creation
    #[arg(short, long)]
    push: bool,

    /// Remote to push to
    #[arg(short, long, default_value = "origin")]
    remote: String,

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

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

impl TagCommand {
    pub async fn execute(&self) -> Result<()> {
        let repo = find_repo(std::env::current_dir()?.as_path())?;
        let manager = ConfigManager::new()?;
        let config = manager.config();

        // Determine tag name
        let tag_name = if let Some(name) = &self.name {
            name.clone()
        } else if let Some(bump_str) = &self.bump {
            // Calculate bumped version
            let prefix = &config.tag.version_prefix;
            let latest = get_latest_version(&repo, prefix)?
                .unwrap_or_else(|| Version::new(0, 0, 0));
            
            let bump = VersionBump::from_str(bump_str)?;
            let new_version = bump_version(&latest, bump, None);
            
            format!("{}{}", prefix, new_version)
        } else {
            // Interactive mode
            self.select_version_interactive(&repo, &config.tag.version_prefix).await?
        };

        // Validate tag name (if it looks like a version)
        if tag_name.starts_with('v') || tag_name.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(false) {
            let version_str = tag_name.trim_start_matches('v');
            if let Err(e) = crate::utils::validators::validate_semver(version_str) {
                println!("{}: {}", "Warning".yellow(), e);
                
                if !self.yes {
                    let proceed = Confirm::new()
                        .with_prompt("Proceed with this tag name anyway?")
                        .default(true)
                        .interact()?;
                    
                    if !proceed {
                        bail!("Tag creation cancelled");
                    }
                }
            }
        }

        // Generate or get tag message
        let message = if self.lightweight {
            None
        } else if let Some(msg) = &self.message {
            Some(msg.clone())
        } else if self.generate || (config.tag.auto_generate && !self.yes) {
            Some(self.generate_tag_message(&repo, &tag_name).await?)
        } else if !self.yes {
            Some(self.input_message_interactive(&tag_name)?)
        } else {
            Some(format!("Release {}", tag_name))
        };

        // Show preview
        println!("\n{}", "─".repeat(60));
        println!("{}", "Tag preview:".bold());
        println!("{}", "─".repeat(60));
        println!("Name:  {}", tag_name.cyan());
        if let Some(ref msg) = message {
            println!("Message:\n{}", msg);
        } else {
            println!("Type:  {}", "lightweight".yellow());
        }
        println!("{}", "─".repeat(60));

        if !self.yes {
            let confirm = Confirm::new()
                .with_prompt("Create this tag?")
                .default(true)
                .interact()?;

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

        if self.dry_run {
            println!("\n{}", "Dry run - tag not created.".yellow());
            return Ok(());
        }

        let builder = TagBuilder::new()
            .name(&tag_name)
            .message_opt(message)
            .annotate(!self.lightweight)
            .sign(self.sign)
            .force(self.force);

        builder.execute(&repo)?;

        println!("{} Created tag {}", "✓".green(), tag_name.cyan());

        // Push if requested
        if self.push {
            println!("{} Pushing tag to {}...", "→".blue(), &self.remote);
            repo.push(&self.remote, &format!("refs/tags/{}", tag_name))?;
            println!("{} Pushed tag to {}", "✓".green(), &self.remote);
        }

        Ok(())
    }

    async fn select_version_interactive(&self, repo: &GitRepo, prefix: &str) -> Result<String> {
        loop {
            let latest = get_latest_version(repo, prefix)?;
            
            println!("\n{}", "Version selection:".bold());
            
            if let Some(ref version) = latest {
                println!("Latest version: {}{}", prefix, version);
            } else {
                println!("No existing version tags found");
            }

            let options = vec![
                "Auto-detect bump from commits",
                "Bump major version",
                "Bump minor version",
                "Bump patch version",
                "Enter custom version",
                "Enter custom tag name",
            ];

            let selection = Select::new()
                .with_prompt("Select option")
                .items(&options)
                .default(0)
                .interact()?;

            match selection {
                0 => {
                    // Auto-detect
                    let commits = repo.get_commits(50)?;
                    let bump = suggest_version_bump(&commits);
                    let version = latest.as_ref()
                        .map(|v| bump_version(v, bump, None))
                        .unwrap_or_else(|| Version::new(0, 1, 0));
                    
                    println!("Suggested bump: {:?} → {}{}", bump, prefix, version);
                    
                    let confirm = Confirm::new()
                        .with_prompt("Use this version?")
                        .default(true)
                        .interact()?;
                    
                    if confirm {
                        return Ok(format!("{}{}", prefix, version));
                    }
                    // User rejected, continue the loop
                }
                1 => {
                    let version = latest.as_ref()
                        .map(|v| bump_version(v, VersionBump::Major, None))
                        .unwrap_or_else(|| Version::new(1, 0, 0));
                    return Ok(format!("{}{}", prefix, version));
                }
                2 => {
                    let version = latest.as_ref()
                        .map(|v| bump_version(v, VersionBump::Minor, None))
                        .unwrap_or_else(|| Version::new(0, 1, 0));
                    return Ok(format!("{}{}", prefix, version));
                }
                3 => {
                    let version = latest.as_ref()
                        .map(|v| bump_version(v, VersionBump::Patch, None))
                        .unwrap_or_else(|| Version::new(0, 0, 1));
                    return Ok(format!("{}{}", prefix, version));
                }
                4 => {
                    let input: String = Input::new()
                        .with_prompt("Enter version (e.g., 1.2.3)")
                        .interact_text()?;
                    let version = Version::parse(&input)?;
                    return Ok(format!("{}{}", prefix, version));
                }
                5 => {
                    let input: String = Input::new()
                        .with_prompt("Enter tag name")
                        .interact_text()?;
                    return Ok(input);
                }
                _ => unreachable!(),
            }
        }
    }

    async fn generate_tag_message(&self, repo: &GitRepo, version: &str) -> Result<String> {
        let manager = ConfigManager::new()?;
        let config = manager.config();

        // Get commits since last tag
        let tags = repo.get_tags()?;
        let commits = if let Some(latest_tag) = tags.first() {
            repo.get_commits_between(&latest_tag.name, "HEAD")?
        } else {
            repo.get_commits(50)?
        };

        if commits.is_empty() {
            return Ok(format!("Release {}", version));
        }

        println!("{} AI is generating tag message from {} commits...", "🤖", commits.len());

        let generator = ContentGenerator::new(&config.llm).await?;
        generator.generate_tag_message(version, &commits).await
    }

    fn input_message_interactive(&self, version: &str) -> Result<String> {
        let default_msg = format!("Release {}", version);
        
        let use_editor = Confirm::new()
            .with_prompt("Open editor for tag message?")
            .default(false)
            .interact()?;
        
        if use_editor {
            crate::utils::editor::edit_content(&default_msg)
        } else {
            Ok(Input::new()
                .with_prompt("Tag message")
                .default(default_msg)
                .interact_text()?)
        }
    }
}

// Helper trait
trait TagBuilderExt {
    fn message_opt(self, message: Option<String>) -> Self;
}

impl TagBuilderExt for TagBuilder {
    fn message_opt(self, message: Option<String>) -> Self {
        if let Some(m) = message {
            self.message(m)
        } else {
            self
        }
    }
}
