//! Pre-commit and post-commit hook generation and installation for scaffolded projects.
//!
//! # TICKET-PMAT-5005: Git Initialization and Pre-commit Hooks
//! # TICKET-PMAT-5013: Auto-update Hooks (Post-commit)
//!
//! This module generates and installs hooks that enforce quality gates
//! and automatically update roadmaps on every commit. Supports both pforge and WASM project types.

use super::config::{QualityGateConfig, TemplateType};
use super::errors::{Result, ScaffoldError};
use std::path::Path;

/// Pre-commit hook configuration
#[derive(Debug, Clone)]
pub struct HookConfig {
    /// Project type (affects which quality gates run)
    pub project_type: TemplateType,
    /// Quality gates to enforce
    pub quality_gates: QualityGateConfig,
}

/// Generate pre-commit hook script for project type
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
pub fn generate_pre_commit_hook(config: &HookConfig) -> Result<String> {
    let script = match &config.project_type {
        TemplateType::Agent { .. } => generate_pforge_hook(config),
        TemplateType::Wasm { .. } => generate_wasm_hook(config),
        _ => return Err(ScaffoldError::UnsupportedProjectType),
    };

    Ok(script)
}

/// Generate pforge-specific pre-commit hook
///
/// # Quality Gates
/// - cargo clippy -- -D warnings
/// - cargo test
/// - Complexity check (if enabled)
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
fn generate_pforge_hook(_config: &HookConfig) -> String {
    r#"#!/bin/bash
# Pre-commit hook for pforge project
# Generated by PMAT ScaffoldEngine

set -e

echo "Running pre-commit quality gates..."

# Clippy (strict mode)
echo "→ Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings

# Tests
echo "→ Running tests..."
cargo test

echo "✓ All quality gates passed!"
exit 0
"#
    .to_string()
}

/// Generate WASM-specific pre-commit hook
///
/// # Quality Gates
/// - cargo clippy -- -D warnings
/// - cargo test
/// - WASM build verification
/// - Complexity check (if enabled)
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
fn generate_wasm_hook(_config: &HookConfig) -> String {
    r#"#!/bin/bash
# Pre-commit hook for WASM project
# Generated by PMAT ScaffoldEngine

set -e

echo "Running pre-commit quality gates..."

# Clippy (strict mode)
echo "→ Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings

# Tests
echo "→ Running tests..."
cargo test

# WASM build verification
echo "→ Verifying WASM build..."
cargo build --target wasm32-unknown-unknown --release

echo "✓ All quality gates passed!"
exit 0
"#
    .to_string()
}

/// Install pre-commit hook to project directory
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 3
pub fn install_pre_commit_hook(project_dir: &Path, script: &str) -> Result<()> {
    use std::fs;

    let hook_path = project_dir.join(".git/hooks/pre-commit");

    // Write hook script
    fs::write(&hook_path, script).map_err(ScaffoldError::IoError)?;

    // Make executable (chmod +x)
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = fs::metadata(&hook_path)
            .map_err(ScaffoldError::IoError)?
            .permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&hook_path, perms).map_err(ScaffoldError::IoError)?;
    }

    Ok(())
}

/// Generate post-commit hook script
///
/// # TICKET-PMAT-5013
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
pub fn generate_post_commit_hook() -> String {
    r#"#!/bin/bash
# Post-commit hook for roadmap updates
# Generated by PMAT Maintenance Engine

# Only run if this is a ticket commit
if ! git log -1 --pretty=%B | grep -q "TICKET-PMAT-"; then
    exit 0
fi

# Run roadmap updater
if command -v pmat &> /dev/null; then
    pmat maintain update-roadmap --from-commit HEAD 2>/dev/null || true
fi

exit 0
"#
    .to_string()
}

/// Install post-commit hook to project directory
///
/// # TICKET-PMAT-5013
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 2
pub fn install_post_commit_hook(project_dir: &Path) -> Result<()> {
    use std::fs;

    let hook_path = project_dir.join(".git/hooks/post-commit");
    let script = generate_post_commit_hook();

    fs::write(&hook_path, script).map_err(ScaffoldError::IoError)?;

    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let mut perms = fs::metadata(&hook_path)
            .map_err(ScaffoldError::IoError)?
            .permissions();
        perms.set_mode(0o755);
        fs::set_permissions(&hook_path, perms).map_err(ScaffoldError::IoError)?;
    }

    Ok(())
}

// ============================================================================
// TICKET-PMAT-5021: Hook Integration with Gate Executor
// ============================================================================

/// Generate pre-commit hook with quality gate executor (fast mode)
///
/// # TICKET-PMAT-5021
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 2
pub fn generate_pre_commit_hook_with_gates_fast() -> String {
    r#"#!/bin/bash
# Pre-commit hook with quality gate executor (fast mode)
# Generated by PMAT ScaffoldEngine - TICKET-PMAT-5021
#
# Fast mode: Runs clippy + tests only (<30s)
# To bypass: git commit --no-verify
# For full gates: Run 'pmat quality-gates' manually

set -e

echo "Running pre-commit quality gates (fast mode)..."

# Clippy (strict mode)
echo "→ Running clippy..."
cargo clippy --all-targets --all-features -- -D warnings

# Tests
echo "→ Running tests..."
cargo test --all-features

echo "✓ All quality gates passed!"
echo "ℹ️  For full coverage/complexity checks, run: pmat quality-gates"
exit 0
"#
    .to_string()
}

/// Generate .pmat-gates.toml configuration file
///
/// # TICKET-PMAT-5021
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 1
pub fn generate_gate_config_toml() -> String {
    r#"# PMAT Quality Gate Configuration
# Generated by PMAT ScaffoldEngine

[gates]
run_clippy = true
clippy_strict = true
run_tests = true
test_timeout = 300
check_coverage = true
min_coverage = 80.0
check_complexity = true
max_complexity = 10
"#
    .to_string()
}

/// Install quality gate configuration file
///
/// # TICKET-PMAT-5021
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 2
pub fn install_gate_config(project_dir: &Path) -> Result<()> {
    use std::fs;

    let config_path = project_dir.join(".pmat-gates.toml");
    let toml = generate_gate_config_toml();

    fs::write(&config_path, toml).map_err(ScaffoldError::IoError)?;

    Ok(())
}

/// Install all hooks (pre-commit, post-commit) with quality gates
///
/// # TICKET-PMAT-5021
///
/// # Complexity
/// - Time: O(1)
/// - Cyclomatic: 2
pub fn install_all_hooks_with_gates(project_dir: &Path) -> Result<()> {
    // Install pre-commit hook with gates (fast mode)
    let pre_commit = generate_pre_commit_hook_with_gates_fast();
    install_pre_commit_hook(project_dir, &pre_commit)?;

    // Install post-commit hook (from PMAT-5013)
    install_post_commit_hook(project_dir)?;

    // Install gate configuration
    install_gate_config(project_dir)?;

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::scaffold::config::AgentFramework;
    use crate::scaffold::config::WasmFramework;
    use std::fs;
    use tempfile::TempDir;

    #[test]
    fn test_generate_pforge_hook() {
        let config = HookConfig {
            project_type: TemplateType::Agent {
                based_on: AgentFramework::Pforge,
            },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();

        assert!(script.contains("#!/bin/bash"));
        assert!(script.contains("cargo clippy"));
        assert!(script.contains("cargo test"));
        assert!(script.contains("pforge project"));
    }

    #[test]
    fn test_generate_wasm_hook() {
        let config = HookConfig {
            project_type: TemplateType::Wasm {
                based_on: WasmFramework::WasmLabs,
            },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();

        assert!(script.contains("#!/bin/bash"));
        assert!(script.contains("cargo clippy"));
        assert!(script.contains("cargo test"));
        assert!(script.contains("wasm32-unknown-unknown"));
        assert!(script.contains("WASM project"));
    }

    #[test]
    fn test_install_pre_commit_hook() {
        let temp_dir = TempDir::new().unwrap();
        let git_dir = temp_dir.path().join(".git/hooks");
        fs::create_dir_all(&git_dir).unwrap();

        let script = "#!/bin/bash\necho test";
        install_pre_commit_hook(temp_dir.path(), script).unwrap();

        let hook_path = git_dir.join("pre-commit");
        assert!(hook_path.exists());

        let content = fs::read_to_string(&hook_path).unwrap();
        assert_eq!(content, script);

        // Check executable permission on Unix
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let perms = fs::metadata(&hook_path).unwrap().permissions();
            assert!(perms.mode() & 0o111 != 0, "Hook should be executable");
        }
    }

    #[test]
    fn test_hook_script_format() {
        let config = HookConfig {
            project_type: TemplateType::Agent {
                based_on: AgentFramework::Pforge,
            },
            quality_gates: QualityGateConfig::extreme_tdd(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();
        assert!(script.contains("All quality gates passed"));
        assert!(script.contains("set -e")); // Fail fast
    }

    #[test]
    fn test_generate_post_commit_hook() {
        let hook = generate_post_commit_hook();

        assert!(hook.starts_with("#!/bin/bash"));
        assert!(hook.contains("TICKET-PMAT-"));
        assert!(hook.contains("pmat maintain update-roadmap"));
    }

    #[test]
    fn test_install_post_commit_hook() {
        let temp_dir = TempDir::new().unwrap();
        let git_dir = temp_dir.path().join(".git/hooks");
        fs::create_dir_all(&git_dir).unwrap();

        install_post_commit_hook(temp_dir.path()).unwrap();

        let hook_path = git_dir.join("post-commit");
        assert!(hook_path.exists());

        let content = fs::read_to_string(&hook_path).unwrap();
        assert!(content.contains("#!/bin/bash"));
        assert!(content.contains("TICKET-PMAT-"));

        // Check executable permission on Unix
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let perms = fs::metadata(&hook_path).unwrap().permissions();
            assert!(perms.mode() & 0o111 != 0, "Hook should be executable");
        }
    }

    #[test]
    fn test_post_commit_hook_silent_failure() {
        let hook = generate_post_commit_hook();
        // Property: Hook should not block commits on failure
        assert!(hook.contains("|| true"));
        assert!(hook.contains("2>/dev/null"));
    }

    // TICKET-PMAT-5021 tests
    #[test]
    fn test_generate_hook_with_gates_fast() {
        let hook = generate_pre_commit_hook_with_gates_fast();

        assert!(hook.starts_with("#!/bin/bash"));
        assert!(hook.contains("TICKET-PMAT-5021"));
        assert!(hook.contains("cargo clippy"));
        assert!(hook.contains("cargo test"));
        assert!(hook.contains("fast mode"));
        assert!(hook.contains("--no-verify"));
    }

    #[test]
    fn test_generate_gate_config_toml() {
        let toml = generate_gate_config_toml();

        assert!(toml.contains("run_clippy = true"));
        assert!(toml.contains("min_coverage = 80.0"));
        assert!(toml.contains("max_complexity = 10"));
        assert!(toml.contains("[gates]"));
    }

    #[test]
    fn test_install_gate_config() {
        let temp_dir = TempDir::new().unwrap();

        install_gate_config(temp_dir.path()).unwrap();

        let config_path = temp_dir.path().join(".pmat-gates.toml");
        assert!(config_path.exists());

        let content = fs::read_to_string(&config_path).unwrap();
        assert!(content.contains("run_clippy"));
        assert!(content.contains("min_coverage"));
    }

    #[test]
    fn test_install_all_hooks_with_gates() {
        let temp_dir = TempDir::new().unwrap();
        let git_dir = temp_dir.path().join(".git/hooks");
        fs::create_dir_all(&git_dir).unwrap();

        install_all_hooks_with_gates(temp_dir.path()).unwrap();

        // Check pre-commit hook
        let pre_commit = git_dir.join("pre-commit");
        assert!(pre_commit.exists());
        let content = fs::read_to_string(&pre_commit).unwrap();
        assert!(content.contains("fast mode"));

        // Check post-commit hook
        let post_commit = git_dir.join("post-commit");
        assert!(post_commit.exists());

        // Check gate config
        let config = temp_dir.path().join(".pmat-gates.toml");
        assert!(config.exists());

        // Check executability
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt;
            let perms = fs::metadata(&pre_commit).unwrap().permissions();
            assert!(perms.mode() & 0o111 != 0, "Pre-commit should be executable");
        }
    }

    #[test]
    fn test_hook_bypass_documentation() {
        let hook = generate_pre_commit_hook_with_gates_fast();
        assert!(hook.contains("--no-verify"));
        assert!(hook.contains("To bypass:"));
    }

    #[test]
    fn test_hook_execution_time_hint() {
        let hook = generate_pre_commit_hook_with_gates_fast();
        assert!(hook.contains("<30s") || hook.contains("fast"));
    }
}

#[cfg(test)]
mod property_tests {
    use super::*;
    use crate::scaffold::config::{AgentFramework, WasmFramework};
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn prop_hooks_are_valid_bash(is_agent in any::<bool>()) {
            let project_type = if is_agent {
                TemplateType::Agent { based_on: AgentFramework::Pforge }
            } else {
                TemplateType::Wasm { based_on: WasmFramework::WasmLabs }
            };

            let config = HookConfig {
                project_type,
                quality_gates: QualityGateConfig::default(),
            };

            let script = generate_pre_commit_hook(&config).unwrap();

            // Property: All hooks are valid bash scripts
            prop_assert!(script.starts_with("#!/bin/bash"));
            prop_assert!(script.contains("set -e"));
            prop_assert!(script.contains("exit 0"));
        }

        #[test]
        fn prop_hooks_check_clippy_and_tests(is_agent in any::<bool>()) {
            let project_type = if is_agent {
                TemplateType::Agent { based_on: AgentFramework::Pforge }
            } else {
                TemplateType::Wasm { based_on: WasmFramework::WasmLabs }
            };

            let config = HookConfig {
                project_type,
                quality_gates: QualityGateConfig::default(),
            };

            let script = generate_pre_commit_hook(&config).unwrap();

            // Property: All hooks enforce clippy and tests
            prop_assert!(script.contains("cargo clippy"));
            prop_assert!(script.contains("cargo test"));
        }
    }

    #[test]
    fn prop_wasm_hooks_verify_wasm_build() {
        // Simple property test (not using proptest macro)
        let config = HookConfig {
            project_type: TemplateType::Wasm {
                based_on: WasmFramework::WasmLabs,
            },
            quality_gates: QualityGateConfig::default(),
        };

        let script = generate_pre_commit_hook(&config).unwrap();

        // Property: WASM hooks verify WASM builds
        assert!(script.contains("wasm32-unknown-unknown"));
    }
}
