//! Pack Test Repository
//!
//! Database operations for pack test execution tracking.

use crate::error::Result;
use crate::models::{Id, PackLatestTest, PackTestExecution, PackTestResult, PackTestStats};
use sqlx::{PgPool, Row};

/// Repository for pack test operations
pub struct PackTestRepository {
    pool: PgPool,
}

impl PackTestRepository {
    /// Create a new pack test repository
    pub fn new(pool: PgPool) -> Self {
        Self { pool }
    }

    /// Create a new pack test execution record
    pub async fn create(
        &self,
        pack_id: Id,
        pack_version: &str,
        trigger_reason: &str,
        result: &PackTestResult,
    ) -> Result<PackTestExecution> {
        let result_json = serde_json::to_value(result)?;

        let record = sqlx::query_as::<_, PackTestExecution>(
            r#"
            INSERT INTO pack_test_execution (
                pack_id, pack_version, execution_time, trigger_reason,
                total_tests, passed, failed, skipped, pass_rate, duration_ms, result
            )
            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
            RETURNING *
            "#,
        )
        .bind(pack_id)
        .bind(pack_version)
        .bind(result.execution_time)
        .bind(trigger_reason)
        .bind(result.total_tests)
        .bind(result.passed)
        .bind(result.failed)
        .bind(result.skipped)
        .bind(result.pass_rate)
        .bind(result.duration_ms)
        .bind(result_json)
        .fetch_one(&self.pool)
        .await?;

        Ok(record)
    }

    /// Find pack test execution by ID
    pub async fn find_by_id(&self, id: Id) -> Result<Option<PackTestExecution>> {
        let record = sqlx::query_as::<_, PackTestExecution>(
            r#"
            SELECT * FROM pack_test_execution
            WHERE id = $1
            "#,
        )
        .bind(id)
        .fetch_optional(&self.pool)
        .await?;

        Ok(record)
    }

    /// List all test executions for a pack
    pub async fn list_by_pack(
        &self,
        pack_id: Id,
        limit: i64,
        offset: i64,
    ) -> Result<Vec<PackTestExecution>> {
        let records = sqlx::query_as::<_, PackTestExecution>(
            r#"
            SELECT * FROM pack_test_execution
            WHERE pack_id = $1
            ORDER BY execution_time DESC
            LIMIT $2 OFFSET $3
            "#,
        )
        .bind(pack_id)
        .bind(limit)
        .bind(offset)
        .fetch_all(&self.pool)
        .await?;

        Ok(records)
    }

    /// Get latest test execution for a pack
    pub async fn get_latest_by_pack(&self, pack_id: Id) -> Result<Option<PackTestExecution>> {
        let record = sqlx::query_as::<_, PackTestExecution>(
            r#"
            SELECT * FROM pack_test_execution
            WHERE pack_id = $1
            ORDER BY execution_time DESC
            LIMIT 1
            "#,
        )
        .bind(pack_id)
        .fetch_optional(&self.pool)
        .await?;

        Ok(record)
    }

    /// Get latest test for all packs
    pub async fn get_all_latest(&self) -> Result<Vec<PackLatestTest>> {
        let records = sqlx::query_as::<_, PackLatestTest>(
            r#"
            SELECT * FROM pack_latest_test
            ORDER BY test_time DESC
            "#,
        )
        .fetch_all(&self.pool)
        .await?;

        Ok(records)
    }

    /// Get test statistics for a pack
    pub async fn get_stats(&self, pack_id: Id) -> Result<PackTestStats> {
        let row = sqlx::query(
            r#"
            SELECT * FROM get_pack_test_stats($1)
            "#,
        )
        .bind(pack_id)
        .fetch_one(&self.pool)
        .await?;

        Ok(PackTestStats {
            total_executions: row.get("total_executions"),
            successful_executions: row.get("successful_executions"),
            failed_executions: row.get("failed_executions"),
            avg_pass_rate: row.get("avg_pass_rate"),
            avg_duration_ms: row.get("avg_duration_ms"),
            last_test_time: row.get("last_test_time"),
            last_test_passed: row.get("last_test_passed"),
        })
    }

    /// Check if pack has recent passing tests
    pub async fn has_passing_tests(&self, pack_id: Id, hours_ago: i32) -> Result<bool> {
        let row = sqlx::query(
            r#"
            SELECT pack_has_passing_tests($1, $2) as has_passing
            "#,
        )
        .bind(pack_id)
        .bind(hours_ago)
        .fetch_one(&self.pool)
        .await?;

        Ok(row.get("has_passing"))
    }

    /// Count test executions by pack
    pub async fn count_by_pack(&self, pack_id: Id) -> Result<i64> {
        let row = sqlx::query(
            r#"
            SELECT COUNT(*) as count FROM pack_test_execution
            WHERE pack_id = $1
            "#,
        )
        .bind(pack_id)
        .fetch_one(&self.pool)
        .await?;

        Ok(row.get("count"))
    }

    /// List test executions by trigger reason
    pub async fn list_by_trigger_reason(
        &self,
        trigger_reason: &str,
        limit: i64,
        offset: i64,
    ) -> Result<Vec<PackTestExecution>> {
        let records = sqlx::query_as::<_, PackTestExecution>(
            r#"
            SELECT * FROM pack_test_execution
            WHERE trigger_reason = $1
            ORDER BY execution_time DESC
            LIMIT $2 OFFSET $3
            "#,
        )
        .bind(trigger_reason)
        .bind(limit)
        .bind(offset)
        .fetch_all(&self.pool)
        .await?;

        Ok(records)
    }

    /// Get failed test executions for a pack
    pub async fn get_failed_by_pack(
        &self,
        pack_id: Id,
        limit: i64,
    ) -> Result<Vec<PackTestExecution>> {
        let records = sqlx::query_as::<_, PackTestExecution>(
            r#"
            SELECT * FROM pack_test_execution
            WHERE pack_id = $1 AND failed > 0
            ORDER BY execution_time DESC
            LIMIT $2
            "#,
        )
        .bind(pack_id)
        .bind(limit)
        .fetch_all(&self.pool)
        .await?;

        Ok(records)
    }

    /// Delete old test executions (cleanup)
    pub async fn delete_old_executions(&self, days_old: i32) -> Result<u64> {
        let result = sqlx::query(
            r#"
            DELETE FROM pack_test_execution
            WHERE execution_time < NOW() - ($1 || ' days')::INTERVAL
            "#,
        )
        .bind(days_old)
        .execute(&self.pool)
        .await?;

        Ok(result.rows_affected())
    }
}

// TODO: Update these tests to use the new repository API (static methods)
// These tests are currently disabled due to repository refactoring
#[cfg(test)]
#[allow(dead_code)]
mod tests {
    // Disabled - needs update for new repository API
    /*
    async fn setup() -> (PgPool, PackRepository, PackTestRepository) {
        let config = DatabaseConfig::from_env();
        let db = Database::new(&config)
            .await
            .expect("Failed to create database");
        let pool = db.pool().clone();
        let pack_repo = PackRepository::new(pool.clone());
        let test_repo = PackTestRepository::new(pool.clone());
        (pool, pack_repo, test_repo)
    }

    #[tokio::test]
    #[ignore] // Requires database
    async fn test_create_test_execution() {
        let (_pool, pack_repo, test_repo) = setup().await;

        // Create a test pack
        let pack = pack_repo
            .create("test_pack", "Test Pack", "Test pack for testing", "1.0.0")
            .await
            .expect("Failed to create pack");

        // Create test result
        let test_result = PackTestResult {
            pack_ref: "test_pack".to_string(),
            pack_version: "1.0.0".to_string(),
            execution_time: Utc::now(),
            status: TestStatus::Passed,
            total_tests: 10,
            passed: 8,
            failed: 2,
            skipped: 0,
            pass_rate: 0.8,
            duration_ms: 5000,
            test_suites: vec![TestSuiteResult {
                name: "Test Suite 1".to_string(),
                runner_type: "shell".to_string(),
                total: 10,
                passed: 8,
                failed: 2,
                skipped: 0,
                duration_ms: 5000,
                test_cases: vec![
                    TestCaseResult {
                        name: "test_1".to_string(),
                        status: TestStatus::Passed,
                        duration_ms: 500,
                        error_message: None,
                        stdout: Some("Success".to_string()),
                        stderr: None,
                    },
                    TestCaseResult {
                        name: "test_2".to_string(),
                        status: TestStatus::Failed,
                        duration_ms: 300,
                        error_message: Some("Test failed".to_string()),
                        stdout: None,
                        stderr: Some("Error output".to_string()),
                    },
                ],
            }],
        };

        // Create test execution
        let execution = test_repo
            .create(pack.id, "1.0.0", "manual", &test_result)
            .await
            .expect("Failed to create test execution");

        assert_eq!(execution.pack_id, pack.id);
        assert_eq!(execution.total_tests, 10);
        assert_eq!(execution.passed, 8);
        assert_eq!(execution.failed, 2);
        assert_eq!(execution.pass_rate, 0.8);
    }

    #[tokio::test]
    #[ignore] // Requires database
    async fn test_get_latest_by_pack() {
        let (_pool, pack_repo, test_repo) = setup().await;

        // Create a test pack
        let pack = pack_repo
            .create("test_pack_2", "Test Pack 2", "Test pack 2", "1.0.0")
            .await
            .expect("Failed to create pack");

        // Create multiple test executions
        for i in 1..=3 {
            let test_result = PackTestResult {
                pack_ref: "test_pack_2".to_string(),
                pack_version: "1.0.0".to_string(),
                execution_time: Utc::now(),
                total_tests: i,
                passed: i,
                failed: 0,
                skipped: 0,
                pass_rate: 1.0,
                duration_ms: 1000,
                test_suites: vec![],
            };

            test_repo
                .create(pack.id, "1.0.0", "manual", &test_result)
                .await
                .expect("Failed to create test execution");
        }

        // Get latest
        let latest = test_repo
            .get_latest_by_pack(pack.id)
            .await
            .expect("Failed to get latest")
            .expect("No latest found");

        assert_eq!(latest.total_tests, 3);
    }

    #[tokio::test]
    #[ignore] // Requires database
    async fn test_get_stats() {
        let (_pool, pack_repo, test_repo) = setup().await;

        // Create a test pack
        let pack = pack_repo
            .create("test_pack_3", "Test Pack 3", "Test pack 3", "1.0.0")
            .await
            .expect("Failed to create pack");

        // Create test executions
        for _ in 1..=5 {
            let test_result = PackTestResult {
                pack_ref: "test_pack_3".to_string(),
                pack_version: "1.0.0".to_string(),
                execution_time: Utc::now(),
                total_tests: 10,
                passed: 10,
                failed: 0,
                skipped: 0,
                pass_rate: 1.0,
                duration_ms: 2000,
                test_suites: vec![],
            };

            test_repo
                .create(pack.id, "1.0.0", "manual", &test_result)
                .await
                .expect("Failed to create test execution");
        }

        // Get stats
        let stats = test_repo
            .get_stats(pack.id)
            .await
            .expect("Failed to get stats");

        assert_eq!(stats.total_executions, 5);
        assert_eq!(stats.successful_executions, 5);
        assert_eq!(stats.failed_executions, 0);
    }
    */
}
