use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use std::time::Duration;
use crate::config::Language;

pub mod ollama;
pub mod openai;
pub mod anthropic;
pub mod kimi;
pub mod deepseek;
pub mod openrouter;

pub use ollama::OllamaClient;
pub use openai::OpenAiClient;
pub use anthropic::AnthropicClient;
pub use kimi::KimiClient;
pub use deepseek::DeepSeekClient;
pub use openrouter::OpenRouterClient;

/// LLM provider trait
#[async_trait]
pub trait LlmProvider: Send + Sync {
    /// Generate text from prompt
    async fn generate(&self, prompt: &str) -> Result<String>;
    
    /// Generate with system prompt
    async fn generate_with_system(&self, system: &str, user: &str) -> Result<String>;
    
    /// Check if provider is available
    async fn is_available(&self) -> bool;
    
    /// Get provider name
    fn name(&self) -> &str;
}

/// LLM client that wraps different providers
pub struct LlmClient {
    provider: Box<dyn LlmProvider>,
    config: LlmClientConfig,
}

#[derive(Debug, Clone)]
pub struct LlmClientConfig {
    pub max_tokens: u32,
    pub temperature: f32,
    pub timeout: Duration,
    pub thinking_enabled: bool,
}

impl Default for LlmClientConfig {
    fn default() -> Self {
        Self {
            max_tokens: 500,
            temperature: 0.7,
            timeout: Duration::from_secs(30),
            thinking_enabled: false,
        }
    }
}

impl LlmClient {
    /// Create LLM client from configuration manager
    pub async fn from_config(manager: &crate::config::manager::ConfigManager) -> Result<Self> {
        let config = manager.config();
        let client_config = LlmClientConfig {
            max_tokens: config.llm.max_tokens,
            temperature: config.llm.temperature,
            timeout: Duration::from_secs(config.llm.timeout),
            thinking_enabled: config.llm.thinking_enabled,
        };

        let provider = config.llm.provider.as_str();
        let model = config.llm.model.as_str();
        let base_url = manager.llm_base_url();
        let api_key = manager.get_api_key();
        let thinking_enabled = config.llm.thinking_enabled;

        let provider: Box<dyn LlmProvider> = match provider {
            "ollama" => {
                Box::new(OllamaClient::new(&base_url, model))
            }
            "openai" => {
                let key = api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("OpenAI API key not configured"))?;
                Box::new(OpenAiClient::new(&base_url, key, model)?)
            }
            "anthropic" => {
                let key = api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("Anthropic API key not configured"))?;
                Box::new(AnthropicClient::new(key, model)?)
            }
            "kimi" => {
                let key = api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("Kimi API key not configured"))?;
                Box::new(KimiClient::with_base_url(key, model, &base_url)?.with_thinking(thinking_enabled))
            }
            "deepseek" => {
                let key = api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("DeepSeek API key not configured"))?;
                Box::new(DeepSeekClient::with_base_url(key, model, &base_url)?.with_thinking(thinking_enabled))
            }
            "openrouter" => {
                let key = api_key.as_ref()
                    .ok_or_else(|| anyhow::anyhow!("OpenRouter API key not configured"))?;
                Box::new(OpenRouterClient::with_base_url(key, model, &base_url)?)
            }
            _ => bail!("Unknown LLM provider: {}", provider),
        };

        Ok(Self {
            provider,
            config: client_config,
        })
    }

    /// Create with specific provider
    pub fn with_provider(provider: Box<dyn LlmProvider>) -> Self {
        Self {
            provider,
            config: LlmClientConfig::default(),
        }
    }

    /// Generate commit message from git diff
    pub async fn generate_commit_message(
        &self,
        diff: &str,
        format: crate::config::CommitFormat,
        language: Language,
    ) -> Result<GeneratedCommit> {
        let system_prompt = get_commit_system_prompt(format, language);
        
        // Add language instruction to the prompt
        let language_instruction = match language {
            Language::Chinese => "\n\n请用中文生成提交消息。",
            Language::Japanese => "\n\n日本語でコミットメッセージを生成してください。",
            Language::Korean => "\n\n한국어로 커밋 메시지를 생성하세요.",
            Language::Spanish => "\n\nPor favor, genera el mensaje de commit en español.",
            Language::French => "\n\nVeuillez générer le message de commit en français.",
            Language::German => "\n\nBitte generieren Sie die Commit-Nachricht auf Deutsch.",
            Language::English => "",
        };
        
        let prompt = format!("{}{}", diff, language_instruction);
        let response = self.provider.generate_with_system(system_prompt, &prompt).await?;
        
        self.parse_commit_response(&response, format)
    }

    /// Generate tag message from commits
    pub async fn generate_tag_message(
        &self,
        version: &str,
        commits: &[String],
        language: Language,
    ) -> Result<String> {
        let system_prompt = get_tag_system_prompt(language);
        let commits_text = commits.join("\n");
        
        // Add language instruction to the prompt
        let language_instruction = match language {
            Language::Chinese => "\n\n请用中文生成标签消息。",
            Language::Japanese => "\n\n日本語でタグメッセージを生成してください。",
            Language::Korean => "\n\n한국어로 태그 메시지를 생성하세요.",
            Language::Spanish => "\n\nPor favor, genera el mensaje de etiqueta en español.",
            Language::French => "\n\nVeuillez générer le message de balise en français.",
            Language::German => "\n\nBitte generieren Sie die Tag-Nachricht auf Deutsch.",
            Language::English => "",
        };
        
        let prompt = format!("Version: {}\n\nCommits:\n{}{}", version, commits_text, language_instruction);
        
        self.provider.generate_with_system(system_prompt, &prompt).await
    }

    /// Generate changelog entry
    pub async fn generate_changelog_entry(
        &self,
        version: &str,
        commits: &[(String, String)], // (type, message)
        language: Language,
    ) -> Result<String> {
        let system_prompt = get_changelog_system_prompt(language);
        
        let commits_text = commits
            .iter()
            .map(|(t, m)| format!("- [{}] {}", t, m))
            .collect::<Vec<_>>()
            .join("\n");
        
        // Add language instruction to the prompt
        let language_instruction = match language {
            Language::Chinese => "\n\n请用中文生成变更日志。",
            Language::Japanese => "\n\n日本語で変更ログを生成してください。",
            Language::Korean => "\n\n한국어로 변경 로그를 생성하세요.",
            Language::Spanish => "\n\nPor favor, genera el registro de cambios en español.",
            Language::French => "\n\nVeuillez générer le journal des modifications en français.",
            Language::German => "\n\nBitte generieren Sie das Changelog auf Deutsch.",
            Language::English => "",
        };
        
        let prompt = format!("Version: {}\n\nCommits:\n{}{}", version, commits_text, language_instruction);
        
        self.provider.generate_with_system(system_prompt, &prompt).await
    }

    /// Check if provider is available
    pub async fn is_available(&self) -> bool {
        self.provider.is_available().await
    }

    /// Parse commit response from LLM
    fn parse_commit_response(&self, response: &str, format: crate::config::CommitFormat) -> Result<GeneratedCommit> {
        let lines: Vec<&str> = response.lines()
            .map(|l| l.trim())
            .filter(|l| !l.is_empty())
            .collect();

        if lines.is_empty() {
            let preview: String = response.chars().take(200).collect();
            bail!(
                "LLM returned empty or whitespace-only response. \
                 Raw response preview: '{}'. \
                 Hint: If using DeepSeek/Kimi with thinking enabled, \
                 the model may have returned reasoning_content only. \
                 Try disabling thinking mode or switching models.",
                preview
            );
        }

        let first_line = lines[0];
        
        // Parse based on format
        match format {
            crate::config::CommitFormat::Conventional => {
                self.parse_conventional_commit(first_line, lines)
            }
            crate::config::CommitFormat::Commitlint => {
                self.parse_commitlint_commit(first_line, lines)
            }
        }
    }

    fn parse_conventional_commit(
        &self,
        first_line: &str,
        lines: Vec<&str>,
    ) -> Result<GeneratedCommit> {
        // Parse: type(scope)!: description
        let parts: Vec<&str> = first_line.splitn(2, ':').collect();
        if parts.len() != 2 {
            bail!("Invalid conventional commit format: missing colon");
        }

        let type_part = parts[0];
        let description = parts[1].trim();

        // Extract type, scope, and breaking indicator
        let breaking = type_part.ends_with('!');
        let type_part = type_part.trim_end_matches('!');

        let (commit_type, scope) = if let Some(start) = type_part.find('(') {
            if let Some(end) = type_part.find(')') {
                let t = &type_part[..start];
                let s = &type_part[start + 1..end];
                (t.to_string(), Some(s.to_string()))
            } else {
                bail!("Invalid scope format: missing closing parenthesis");
            }
        } else {
            (type_part.to_string(), None)
        };

        // Extract body and footer
        let (body, footer) = self.extract_body_footer(&lines);

        Ok(GeneratedCommit {
            commit_type,
            scope,
            description: description.to_string(),
            body,
            footer,
            breaking,
        })
    }

    fn parse_commitlint_commit(
        &self,
        first_line: &str,
        lines: Vec<&str>,
    ) -> Result<GeneratedCommit> {
        // Similar parsing but with commitlint rules
        let parts: Vec<&str> = first_line.splitn(2, ':').collect();
        if parts.len() != 2 {
            bail!("Invalid commit format: missing colon");
        }

        let type_part = parts[0];
        let subject = parts[1].trim();

        let (commit_type, scope) = if let Some(start) = type_part.find('(') {
            if let Some(end) = type_part.find(')') {
                let t = &type_part[..start];
                let s = &type_part[start + 1..end];
                (t.to_string(), Some(s.to_string()))
            } else {
                (type_part.to_string(), None)
            }
        } else {
            (type_part.to_string(), None)
        };

        let (body, footer) = self.extract_body_footer(&lines);

        Ok(GeneratedCommit {
            commit_type,
            scope,
            description: subject.to_string(),
            body,
            footer,
            breaking: false,
        })
    }

    fn extract_body_footer(&self, lines: &[&str]) -> (Option<String>, Option<String>) {
        if lines.len() <= 1 {
            return (None, None);
        }

        let rest: Vec<&str> = lines[1..]
            .iter()
            .skip_while(|l| l.trim().is_empty())
            .copied()
            .collect();

        if rest.is_empty() {
            return (None, None);
        }

        // Look for footer markers
        let footer_markers = ["BREAKING CHANGE:", "Closes", "Fixes", "Refs", "Co-authored-by:"];
        
        let mut body_lines = vec![];
        let mut footer_lines = vec![];
        let mut in_footer = false;

        for line in &rest {
            if footer_markers.iter().any(|m| line.starts_with(m)) {
                in_footer = true;
            }
            
            if in_footer {
                footer_lines.push(*line);
            } else {
                body_lines.push(*line);
            }
        }

        let body = if body_lines.is_empty() {
            None
        } else {
            Some(body_lines.join("\n"))
        };

        let footer = if footer_lines.is_empty() {
            None
        } else {
            Some(footer_lines.join("\n"))
        };

        (body, footer)
    }
}

/// Generated commit structure
#[derive(Debug, Clone)]
pub struct GeneratedCommit {
    pub commit_type: String,
    pub scope: Option<String>,
    pub description: String,
    pub body: Option<String>,
    pub footer: Option<String>,
    pub breaking: bool,
}

impl GeneratedCommit {
    /// Format as conventional commit
    pub fn to_conventional(&self) -> String {
        crate::utils::formatter::format_conventional_commit(
            &self.commit_type,
            self.scope.as_deref(),
            &self.description,
            self.body.as_deref(),
            self.footer.as_deref(),
            self.breaking,
        )
    }

    /// Format as commitlint commit
    pub fn to_commitlint(&self) -> String {
        crate::utils::formatter::format_commitlint_commit(
            &self.commit_type,
            self.scope.as_deref(),
            &self.description,
            self.body.as_deref(),
            self.footer.as_deref(),
            None,
        )
    }
}

/// HTTP client helper
pub(crate) fn create_http_client(timeout: Duration) -> Result<reqwest::Client> {
    reqwest::Client::builder()
        .timeout(timeout)
        .build()
        .context("Failed to create HTTP client")
}

/// Get commit system prompt based on format and language
fn get_commit_system_prompt(format: crate::config::CommitFormat, language: Language) -> &'static str {
    match (format, language) {
        (crate::config::CommitFormat::Conventional, Language::Chinese) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_ZH,
        (crate::config::CommitFormat::Conventional, Language::Japanese) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_JA,
        (crate::config::CommitFormat::Conventional, Language::Korean) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_KO,
        (crate::config::CommitFormat::Conventional, Language::Spanish) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_ES,
        (crate::config::CommitFormat::Conventional, Language::French) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_FR,
        (crate::config::CommitFormat::Conventional, Language::German) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT_DE,
        (crate::config::CommitFormat::Conventional, _) => CONVENTIONAL_COMMIT_SYSTEM_PROMPT,
        (crate::config::CommitFormat::Commitlint, Language::Chinese) => COMMITLINT_SYSTEM_PROMPT_ZH,
        (crate::config::CommitFormat::Commitlint, Language::Japanese) => COMMITLINT_SYSTEM_PROMPT_JA,
        (crate::config::CommitFormat::Commitlint, Language::Korean) => COMMITLINT_SYSTEM_PROMPT_KO,
        (crate::config::CommitFormat::Commitlint, Language::Spanish) => COMMITLINT_SYSTEM_PROMPT_ES,
        (crate::config::CommitFormat::Commitlint, Language::French) => COMMITLINT_SYSTEM_PROMPT_FR,
        (crate::config::CommitFormat::Commitlint, Language::German) => COMMITLINT_SYSTEM_PROMPT_DE,
        (crate::config::CommitFormat::Commitlint, _) => COMMITLINT_SYSTEM_PROMPT,
    }
}

fn get_tag_system_prompt(language: Language) -> &'static str {
    match language {
        Language::Chinese => TAG_MESSAGE_SYSTEM_PROMPT_ZH,
        Language::Japanese => TAG_MESSAGE_SYSTEM_PROMPT_JA,
        Language::Korean => TAG_MESSAGE_SYSTEM_PROMPT_KO,
        Language::Spanish => TAG_MESSAGE_SYSTEM_PROMPT_ES,
        Language::French => TAG_MESSAGE_SYSTEM_PROMPT_FR,
        Language::German => TAG_MESSAGE_SYSTEM_PROMPT_DE,
        _ => TAG_MESSAGE_SYSTEM_PROMPT,
    }
}

fn get_changelog_system_prompt(language: Language) -> &'static str {
    match language {
        Language::Chinese => CHANGELOG_SYSTEM_PROMPT_ZH,
        Language::Japanese => CHANGELOG_SYSTEM_PROMPT_JA,
        Language::Korean => CHANGELOG_SYSTEM_PROMPT_KO,
        Language::Spanish => CHANGELOG_SYSTEM_PROMPT_ES,
        Language::French => CHANGELOG_SYSTEM_PROMPT_FR,
        Language::German => CHANGELOG_SYSTEM_PROMPT_DE,
        _ => CHANGELOG_SYSTEM_PROMPT,
    }
}

// System prompts for LLM

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT: &str = r#"You are a helpful assistant that generates conventional commit messages.

Analyze the git diff provided and generate a commit message following the Conventional Commits specification.

Format: <type>[optional scope]: <description>

Types:
- feat: A new feature
- fix: A bug fix
- docs: Documentation only changes
- style: Changes that don't affect code meaning (formatting, semicolons, etc.)
- refactor: Code change that neither fixes a bug nor adds a feature
- perf: Code change that improves performance
- test: Adding or correcting tests
- build: Changes to build system or dependencies
- ci: Changes to CI configuration
- chore: Other changes that don't modify src or test files
- revert: Reverts a previous commit

Rules:
1. Use lowercase for type and scope
2. Keep description under 100 characters
3. Use imperative mood ("add" not "added")
4. Don't capitalize first letter
5. No period at the end
6. Include scope if the change is specific to a module/component

Output ONLY the commit message, nothing else.
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_ZH: &str = r#"你是一个生成符合 Conventional Commits 规范的提交消息的助手。

分析提供的 git diff，并生成符合 Conventional Commits 规范的提交消息。

格式: <type>[可选作用域]: <描述>

类型:
- feat: 新功能
- fix: 修复错误
- docs: 仅文档更改
- style: 不影响代码含义的更改（格式化、分号等）
- refactor: 既不修复错误也不添加功能的代码更改
- perf: 提高性能的代码更改
- test: 添加或更正测试
- build: 更改构建系统或依赖项
- ci: 更改 CI 配置
- chore: 其他不修改 src 或测试文件的更改
- revert: 撤销之前的提交

规则:
1. 类型和小写使用小写
2. 描述保持在 100 个字符以内
3. 使用祈使语气（"添加"而不是"已添加"）
4. 不要大写首字母
5. 结尾不要句号
6. 如果更改特定于模块/组件，请包含作用域

仅输出提交消息，不要输出其他内容。
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_JA: &str = r#"あなたはConventional Commits仕様に従ったコミットメッセージを生成するアシスタントです。

提供されたgit diffを分析し、Conventional Commits仕様に従ったコミットメッセージを生成してください。

形式: <type>[オプションのスコープ]: <説明>

タイプ:
- feat: 新機能
- fix: バグ修正
- docs: ドキュメントのみの変更
- style: コードの意味に影響しない変更（フォーマット、セミコロンなど）
- refactor: バグ修正や機能追加を伴わないコード変更
- perf: パフォーマンスを向上させるコード変更
- test: テストの追加または修正
- build: ビルドシステムまたは依存関係の変更
- ci: CI設定の変更
- chore: srcやテストファイルを変更しないその他の変更
- revert: 以前のコミットを取り消す

ルール:
1. タイプとスコープは小文字を使用
2. 説明は100文字以内にする
3. 命令形を使用する（"追加"ではなく"追加する"）
4. 先頭を大文字にしない
5. 最後にピリオドを付けない
6. 変更がモジュール/コンポーネントに固有の場合はスコープを含める

コミットメッセージのみを出力し、それ以外は出力しないでください。
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_KO: &str = r#"당신은 Conventional Commits 사양에 따른 커밋 메시지를 생성하는 도우미입니다.

제공된 git diff를 분석하고 Conventional Commits 사양에 따른 커밋 메시지를 생성하세요.

형식: <type>[선택적 범위]: <설명>

유형:
- feat: 새 기능
- fix: 버그 수정
- docs: 문서 변경만
- style: 코드 의미에 영향을 주지 않는 변경(서식, 세미콜론 등)
- refactor: 버그를 수정하거나 기능을 추가하지 않는 코드 변경
- perf: 성능을 향상시키는 코드 변경
- test: 테스트 추가 또는 수정
- build: 빌드 시스템 또는 종속성 변경
- ci: CI 구성 변경
- chore: src 또는 테스트 파일을 수정하지 않는 기타 변경
- revert: 이전 커밋 되돌리기

규칙:
1. 유형과 범위는 소문자 사용
2. 설명은 100자 이내로 유지
3. 명령형 사용("추가"가 아닌 "추가하다")
4. 첫 글자 대문자화하지 않음
5. 끝에 마침표 사용하지 않음
6. 변경 사항이 모듈/구성 요소에 특정한 경우 범위 포함

커밋 메시지만 출력하고 다른 내용은 출력하지 마세요.
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_ES: &str = r#"Eres un asistente que genera mensajes de commit siguiendo la especificación Conventional Commits.

Analiza el diff de git proporcionado y genera un mensaje de commit siguiendo la especificación Conventional Commits.

Formato: <tipo>[alcance opcional]: <descripción>

Tipos:
- feat: Una nueva característica
- fix: Una corrección de error
- docs: Solo cambios en documentación
- style: Cambios que no afectan el significado del código (formato, punto y coma, etc.)
- refactor: Cambio de código que no corrige un error ni agrega una característica
- perf: Cambio de código que mejora el rendimiento
- test: Agregar o corregir pruebas
- build: Cambios en el sistema de construcción o dependencias
- ci: Cambios en la configuración de CI
- chore: Otros cambios que no modifican archivos src o de prueba
- revert: Revierte un commit anterior

Reglas:
1. Usa minúsculas para tipo y alcance
2. Mantén la descripción bajo 100 caracteres
3. Usa modo imperativo ("agregar" no "agregado")
4. No capitalices la primera letra
5. Sin punto al final
6. Incluye alcance si el cambio es específico de un módulo/componente

Genera SOLO el mensaje de commit, nada más.
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_FR: &str = r#"Vous êtes un assistant qui génère des messages de commit suivant la spécification Conventional Commits.

Analysez le diff git fourni et générez un message de commit suivant la spécification Conventional Commits.

Format: <type>[portée optionnelle]: <description>

Types:
- feat: Une nouvelle fonctionnalité
- fix: Une correction de bug
- docs: Changements de documentation uniquement
- style: Changements qui n'affectent pas la signification du code (formatage, points-virgules, etc.)
- refactor: Changement de code qui ne corrige pas un bug ni n'ajoute une fonctionnalité
- perf: Changement de code qui améliore les performances
- test: Ajout ou correction de tests
- build: Changements du système de build ou des dépendances
- ci: Changements de la configuration CI
- chore: Autres changements qui ne modifient pas les fichiers src ou de test
- revert: Révertit un commit précédent

Règles:
1. Utilisez des minuscules pour le type et la portée
2. Gardez la description sous 100 caractères
3. Utilisez le mode impératif ("ajouter" non "ajouté")
4. Ne capitalisez pas la première lettre
5. Pas de point à la fin
6. Incluez la portée si le changement est spécifique à un module/composant

Générez SEULEMENT le message de commit, rien d'autre.
"#;

const CONVENTIONAL_COMMIT_SYSTEM_PROMPT_DE: &str = r#"Sie sind ein Assistent, der Commit-Nachrichten gemäß der Conventional Commits-Spezifikation generiert.

Analysieren Sie den bereitgestellten git diff und generieren Sie eine Commit-Nachricht gemäß der Conventional Commits-Spezifikation.

Format: <typ>[optionaler Bereich]: <beschreibung>

Typen:
- feat: Eine neue Funktion
- fix: Ein Bugfix
- docs: Nur Dokumentationsänderungen
- style: Änderungen, die die Code-Bedeutung nicht beeinflussen (Formatierung, Semikolons usw.)
- refactor: Code-Änderung, die weder einen Bug behebt noch eine Funktion hinzufügt
- perf: Code-Änderung, die die Leistung verbessert
- test: Hinzufügen oder Korrigieren von Tests
- build: Änderungen am Build-System oder Abhängigkeiten
- ci: Änderungen an der CI-Konfiguration
- chore: Andere Änderungen, die src- oder Testdateien nicht ändern
- revert: Setzt einen vorherigen Commit zurück

Regeln:
1. Verwenden Sie Kleinbuchstaben für Typ und Bereich
2. Halten Sie die Beschreibung unter 100 Zeichen
3. Verwenden Sie den Imperativ ("hinzufügen" nicht "hinzugefügt")
4. Großschreiben Sie den ersten Buchstaben nicht
5. Kein Punkt am Ende
6. Fügen Sie einen Bereich ein, wenn die Änderung spezifisch für ein Modul/Komponente ist

Geben Sie NUR die Commit-Nachricht aus, nichts anderes.
"#;

const COMMITLINT_SYSTEM_PROMPT: &str = r#"You are a helpful assistant that generates commit messages following @commitlint/config-conventional.

Analyze the git diff and generate a commit message.

Format: <type>[optional scope]: <subject>

Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

Rules:
1. Subject should not start with uppercase
2. Subject should not end with period
3. Subject should be 4-100 characters
4. Use imperative mood
5. Be concise but descriptive

Output ONLY the commit message, nothing else.
"#;

const COMMITLINT_SYSTEM_PROMPT_ZH: &str = r#"你是一个生成符合 @commitlint/config-conventional 规范的提交消息的助手。

分析 git diff 并生成提交消息。

格式: <type>[可选作用域]: <主题>

类型: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

规则:
1. 主题不应以大写字母开头
2. 主题不应以句号结尾
3. 主题应为 4-100 个字符
4. 使用祈使语气
5. 简洁但描述性强

仅输出提交消息，不要输出其他内容。
"#;

const COMMITLINT_SYSTEM_PROMPT_JA: &str = r#"あなたは@commitlint/config-conventionalに従ったコミットメッセージを生成するアシスタントです。

git diffを分析し、コミットメッセージを生成してください。

形式: <type>[オプションのスコープ]: <件名>

タイプ: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

ルール:
1. 件名は大文字で始めないでください
2. 件名はピリオドで終わらないでください
3. 件名は4-100文字である必要があります
4. 命令形を使用してください
5. 簡潔ですが説明的であること

コミットメッセージのみを出力し、それ以外は出力しないでください。
"#;

const COMMITLINT_SYSTEM_PROMPT_KO: &str = r#"당신은 @commitlint/config-conventional에 따른 커밋 메시지를 생성하는 도우미입니다.

git diff를 분석하고 커밋 메시지를 생성하세요.

형식: <type>[선택적 범위]: <제목>

유형: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

규칙:
1. 제목은 대문자로 시작하지 않아야 합니다
2. 제목은 마침표로 끝나지 않아야 합니다
3. 제목은 4-100자여야 합니다
4. 명령형을 사용하세요
5. 간결하지만 설명적이어야 합니다

커밋 메시지만 출력하고 다른 내용은 출력하지 마세요.
"#;

const COMMITLINT_SYSTEM_PROMPT_ES: &str = r#"Eres un asistente que genera mensajes de commit siguiendo @commitlint/config-conventional.

Analiza el diff de git y genera un mensaje de commit.

Formato: <tipo>[alcance opcional]: <asunto>

Tipos: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

Reglas:
1. El asunto no debe comenzar con mayúscula
2. El asunto no debe terminar con punto
3. El asunto debe tener 4-100 caracteres
4. Usa modo imperativo
5. Sé conciso pero descriptivo

Genera SOLO el mensaje de commit, nada más.
"#;

const COMMITLINT_SYSTEM_PROMPT_FR: &str = r#"Vous êtes un assistant qui génère des messages de commit suivant @commitlint/config-conventional.

Analysez le diff git et générez un message de commit.

Format: <type>[portée optionnelle]: <sujet>

Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

Règles:
1. Le sujet ne doit pas commencer par une majuscule
2. Le sujet ne doit pas se terminer par un point
3. Le sujet doit avoir 4-100 caractères
4. Utilisez le mode impératif
5. Soyez concis mais descriptif

Générez SEULEMENT le message de commit, rien d'autre.
"#;

const COMMITLINT_SYSTEM_PROMPT_DE: &str = r#"Sie sind ein Assistent, der Commit-Nachrichten gemäß @commitlint/config-conventional generiert.

Analysieren Sie den git diff und generieren Sie eine Commit-Nachricht.

Format: <typ>[optionaler Bereich]: <betreff>

Typen: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

Regeln:
1. Der Betreff sollte nicht mit einem Großbuchstaben beginnen
2. Der Betreff sollte nicht mit einem Punkt enden
3. Der Betreff sollte 4-100 Zeichen haben
4. Verwenden Sie den Imperativ
5. Seien Sie prägnant aber beschreibend

Geben Sie NUR die Commit-Nachricht aus, nichts anderes.
"#;

const TAG_MESSAGE_SYSTEM_PROMPT: &str = r#"You are a helpful assistant that generates git tag annotation messages.

Given a version number and a list of commits, generate a concise but informative tag message.

The message should:
1. Start with a brief summary of the release
2. Group changes by type (features, fixes, etc.)
3. Be suitable for a git annotated tag

Format:
<version> Release

Summary of changes...

Changes:
- Feature: description
- Fix: description
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_ZH: &str = r#"你是一个生成 git 标签注释消息的助手。

给定版本号和提交列表，生成简洁但信息丰富的标签消息。

消息应该：
1. 以发布的简要摘要开始
2. 按类型分组更改（功能、修复等）
3. 适合 git 标注标签

格式：
<version> 发布

更改摘要...

更改：
- 功能：描述
- 修复：描述
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_JA: &str = r#"あなたはgitタグ注釈メッセージを生成するアシスタントです。

バージョン番号とコミットのリストを考慮して、簡潔ですが情報豊富なタグメッセージを生成してください。

メッセージは以下のようであるべきです：
1. リリースの簡単な要約から始める
2. タイプ別に変更をグループ化する（機能、修正など）
3. git注釈タグに適している

形式：
<version> リリース

変更の概要...

変更：
- 機能：説明
- 修正：説明
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_KO: &str = r#"당신은 git 태그 주석 메시지를 생성하는 도우미입니다.

버전 번호와 커밋 목록을 고려하여 간결하지만 정보가 풍부한 태그 메시지를 생성하세요.

메시지는 다음과 같아야 합니다:
1. 릴리스의 간단한 요약으로 시작
2. 유형별로 변경 사항 그룹화(기능, 수정 등)
3. git 주석 태그에 적합

형식:
<version> 릴리스

변경 사항 요약...

변경 사항:
- 기능: 설명
- 수정: 설명
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_ES: &str = r#"Eres un asistente que genera mensajes de anotación de etiquetas git.

Dado un número de versión y una lista de commits, genera un mensaje de etiqueta conciso pero informativo.

El mensaje debe:
1. Comenzar con un resumen breve de la versión
2. Agrupar cambios por tipo (características, correcciones, etc.)
3. Ser adecuado para una etiqueta git anotada

Formato:
<version> Versión

Resumen de cambios...

Cambios:
- Característica: descripción
- Corrección: descripción
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_FR: &str = r#"Vous êtes un assistant qui génère des messages d'annotation de balises git.

Étant donné un numéro de version et une liste de commits, générez un message de balise concis mais informatif.

Le message doit :
1. Commencer par un bref résumé de la version
2. Grouper les changements par type (fonctionnalités, corrections, etc.)
3. Être adapté à une balise git annotée

Format :
<version> Version

Résumé des changements...

Changements :
- Fonctionnalité : description
- Correction : description
...
"#;

const TAG_MESSAGE_SYSTEM_PROMPT_DE: &str = r#"Sie sind ein Assistent, der git-Tag-Anmerkungsnachrichten generiert.

Gegeben eine Versionsnummer und eine Liste von Commits, generieren Sie eine prägnante aber informative Tag-Nachricht.

Die Nachricht sollte:
1. Mit einer kurzen Zusammenfassung der Version beginnen
2. Änderungen nach Typ gruppieren (Funktionen, Fixes, etc.)
3. Für ein git-annotiertes Tag geeignet sein

Format:
<version> Version

Zusammenfassung der Änderungen...

Änderungen:
- Funktion: Beschreibung
- Fix: Beschreibung
...
"#;

const CHANGELOG_SYSTEM_PROMPT: &str = r#"You are a helpful assistant that generates changelog entries.

Given a version and a list of commits, generate a well-formatted changelog section.

Group commits by:
- Features (feat)
- Bug Fixes (fix)
- Documentation (docs)
- Other Changes

Format in markdown with proper headings and bullet points.
"#;

const CHANGELOG_SYSTEM_PROMPT_ZH: &str = r#"你是一个生成变更日志条目的助手。

给定版本和提交列表，生成格式良好的变更日志部分。

按以下方式分组提交：
- 功能 (feat)
- 错误修复 (fix)
- 文档 (docs)
- 其他更改

使用适当的标题和项目符号以 markdown 格式输出。
"#;

const CHANGELOG_SYSTEM_PROMPT_JA: &str = r#"あなたは変更ログエントリを生成するアシスタントです。

バージョンとコミットのリストを考慮して、適切にフォーマットされた変更ログセクションを生成してください。

コミットを以下でグループ化してください：
- 機能 (feat)
- バグ修正 (fix)
- ドキュメント (docs)
- その他の変更

適切な見出しと箇条書きを使用してmarkdown形式でフォーマットしてください。
"#;

const CHANGELOG_SYSTEM_PROMPT_KO: &str = r#"당신은 변경 로그 항목을 생성하는 도우미입니다.

버전과 커밋 목록을 고려하여 잘 포맷된 변경 로그 섹션을 생성하세요.

다음으로 커밋을 그룹화하세요:
- 기능 (feat)
- 버그 수정 (fix)
- 문서 (docs)
- 기타 변경 사항

적절한 제목과 글머리 기호를 사용하여 markdown 형식으로 포맷하세요.
"#;

const CHANGELOG_SYSTEM_PROMPT_ES: &str = r#"Eres un asistente que genera entradas de registro de cambios.

Dada una versión y una lista de commits, genera una sección de registro de cambios bien formateada.

Agrupa los commits por:
- Características (feat)
- Correcciones de errores (fix)
- Documentación (docs)
- Otros cambios

Formatea en markdown con encabezados y viñetas apropiados.
"#;

const CHANGELOG_SYSTEM_PROMPT_FR: &str = r#"Vous êtes un assistant qui génère des entrées de journal des modifications.

Étant donné une version et une liste de commits, générez une section de journal des modifications bien formatée.

Groupez les commits par :
- Fonctionnalités (feat)
- Corrections de bugs (fix)
- Documentation (docs)
- Autres modifications

Formatez en markdown avec des en-têtes et des puces appropriés.
"#;

const CHANGELOG_SYSTEM_PROMPT_DE: &str = r#"Sie sind ein Assistent, der Changelog-Einträge generiert.

Gegeben eine Version und eine Liste von Commits, generieren Sie einen gut formatierten Changelog-Abschnitt.

Gruppieren Sie Commits nach:
- Funktionen (feat)
- Bugfixes (fix)
- Dokumentation (docs)
- Andere Änderungen

Formatieren Sie in Markdown mit geeigneten Überschriften und Aufzählungspunkten.
"#;

/// Test LLM connection
pub async fn test_connection(manager: &crate::config::manager::ConfigManager) -> Result<String> {
    let client = LlmClient::from_config(manager).await?;
    let response = client.provider.generate("Say 'Hello, World!'").await?;
    Ok(response)
}
