use readur::test_utils::TestContext;
use uuid;

#[cfg(test)]
mod migration_constraint_tests {
    use super::*;

    #[tokio::test]
    async fn test_failed_documents_constraint_validation() {
        let ctx = TestContext::new().await;
        let pool = ctx.state.db.get_pool();
        
        // Create a test user first to avoid foreign key constraint violations
        let user_id = uuid::Uuid::new_v4();
        let unique_suffix = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let username = format!("test_constraint_user_{}", unique_suffix);
        let email = format!("test_constraint_{}@example.com", unique_suffix);
        
        sqlx::query(
            "INSERT INTO users (id, username, email, password_hash, role) 
             VALUES ($1, $2, $3, $4, $5)"
        )
        .bind(user_id)
        .bind(&username)
        .bind(&email)
        .bind("hash")
        .bind("user")
        .execute(pool)
        .await
        .unwrap();
        
        // Test that all allowed failure_reason values work
        let valid_reasons = vec![
            "duplicate_content", "duplicate_filename", "unsupported_format",
            "file_too_large", "file_corrupted", "access_denied", 
            "low_ocr_confidence", "ocr_timeout", "ocr_memory_limit",
            "pdf_parsing_error", "storage_quota_exceeded", "network_error",
            "permission_denied", "virus_detected", "invalid_structure",
            "policy_violation", "other"
        ];

        for reason in valid_reasons {
            let result = sqlx::query(
                r#"
                INSERT INTO failed_documents (
                    user_id, filename, failure_reason, failure_stage, ingestion_source
                ) VALUES (
                    $1, $2, $3, 'validation', 'test'
                )
                "#
            )
            .bind(user_id)
            .bind(format!("test_file_{}.txt", reason))
            .bind(reason)
            .execute(pool)
            .await;

            assert!(result.is_ok(), "Valid failure_reason '{}' should be accepted", reason);
        }
    }

    #[tokio::test]
    async fn test_failed_documents_invalid_constraint_rejection() {
        let ctx = TestContext::new().await;
        let pool = ctx.state.db.get_pool();
        
        // Create a test user first to avoid foreign key constraint violations
        let user_id = uuid::Uuid::new_v4();
        let unique_suffix = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let username = format!("test_invalid_user_{}", unique_suffix);
        let email = format!("test_invalid_{}@example.com", unique_suffix);
        
        sqlx::query(
            "INSERT INTO users (id, username, email, password_hash, role) 
             VALUES ($1, $2, $3, $4, $5)"
        )
        .bind(user_id)
        .bind(&username)
        .bind(&email)
        .bind("hash")
        .bind("user")
        .execute(pool)
        .await
        .unwrap();
        
        // Test that invalid failure_reason values are rejected
        let invalid_reasons = vec![
            "invalid_reason", "unknown", "timeout", "memory_limit", 
            "migration_completed", "corrupted", "unsupported"
        ];

        for reason in invalid_reasons {
            let result = sqlx::query(
                r#"
                INSERT INTO failed_documents (
                    user_id, filename, failure_reason, failure_stage, ingestion_source
                ) VALUES (
                    $1, $2, $3, 'validation', 'test'
                )
                "#
            )
            .bind(user_id)
            .bind(format!("test_file_{}.txt", reason))
            .bind(reason)
            .execute(pool)
            .await;

            assert!(result.is_err(), "Invalid failure_reason '{}' should be rejected", reason);
        }
    }

    #[tokio::test]
    async fn test_failed_documents_stage_constraint_validation() {
        let ctx = TestContext::new().await;
        let pool = ctx.state.db.get_pool();
        
        // Create a test user first to avoid foreign key constraint violations
        let user_id = uuid::Uuid::new_v4();
        let unique_suffix = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let username = format!("test_stage_user_{}", unique_suffix);
        let email = format!("test_stage_{}@example.com", unique_suffix);
        
        sqlx::query(
            "INSERT INTO users (id, username, email, password_hash, role) 
             VALUES ($1, $2, $3, $4, $5)"
        )
        .bind(user_id)
        .bind(&username)
        .bind(&email)
        .bind("hash")
        .bind("user")
        .execute(pool)
        .await
        .unwrap();
        
        // Test that all allowed failure_stage values work
        let valid_stages = vec![
            "ingestion", "validation", "ocr", "storage", "processing", "sync"
        ];

        for stage in valid_stages {
            let result = sqlx::query(
                r#"
                INSERT INTO failed_documents (
                    user_id, filename, failure_reason, failure_stage, ingestion_source
                ) VALUES (
                    $1, $2, 'other', $3, 'test'
                )
                "#
            )
            .bind(user_id)
            .bind(format!("test_file_{}.txt", stage))
            .bind(stage)
            .execute(pool)
            .await;

            assert!(result.is_ok(), "Valid failure_stage '{}' should be accepted", stage);
        }
    }

    #[tokio::test]
    async fn test_migration_mapping_compatibility() {
        let ctx = TestContext::new().await;
        let pool = ctx.state.db.get_pool();
        
        // Create a test user first to avoid foreign key constraint violations
        let user_id = uuid::Uuid::new_v4();
        let unique_suffix = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let username = format!("test_migration_user_{}", unique_suffix);
        let email = format!("test_migration_{}@example.com", unique_suffix);
        
        sqlx::query(
            "INSERT INTO users (id, username, email, password_hash, role) 
             VALUES ($1, $2, $3, $4, $5)"
        )
        .bind(user_id)
        .bind(&username)
        .bind(&email)
        .bind("hash")
        .bind("user")
        .execute(pool)
        .await
        .unwrap();
        
        // Test that the migration mapping logic matches our constraints
        let migration_mappings = vec![
            ("low_ocr_confidence", "low_ocr_confidence"),
            ("timeout", "ocr_timeout"),
            ("memory_limit", "ocr_memory_limit"),
            ("pdf_parsing_error", "pdf_parsing_error"),
            ("corrupted", "file_corrupted"),
            ("file_corrupted", "file_corrupted"),
            ("unsupported_format", "unsupported_format"),
            ("access_denied", "access_denied"),
            ("unknown_value", "other"), // fallback case
            ("", "other"), // empty case
        ];

        for (input_reason, expected_output) in migration_mappings {
            // Simulate the migration CASE logic
            let mapped_reason = match input_reason {
                "low_ocr_confidence" => "low_ocr_confidence",
                "timeout" => "ocr_timeout",
                "memory_limit" => "ocr_memory_limit",
                "pdf_parsing_error" => "pdf_parsing_error",
                "corrupted" | "file_corrupted" => "file_corrupted",
                "unsupported_format" => "unsupported_format",
                "access_denied" => "access_denied",
                _ => "other",
            };

            assert_eq!(mapped_reason, expected_output, 
                      "Migration mapping for '{}' should produce '{}'", 
                      input_reason, expected_output);

            // Test that the mapped value works in the database
            let result = sqlx::query(
                r#"
                INSERT INTO failed_documents (
                    user_id, filename, failure_reason, failure_stage, ingestion_source
                ) VALUES (
                    $1, $2, $3, 'ocr', 'migration'
                )
                "#
            )
            .bind(user_id)
            .bind(format!("migration_test_{}.txt", input_reason.replace("/", "_")))
            .bind(mapped_reason)
            .execute(pool)
            .await;

            assert!(result.is_ok(), 
                   "Mapped failure_reason '{}' (from '{}') should be accepted by constraints", 
                   mapped_reason, input_reason);
        }
    }
}