//! Performance tests for pleme-codegen macros
//! 
//! Ensures generated code performs within 5% of manual implementation
//! and validates zero-cost abstraction guarantees.

use criterion::{black_box, criterion_group, criterion_main, Criterion, BenchmarkId};
use rust_decimal::Decimal;
use std::str::FromStr;
use uuid::Uuid;

// Mock types for performance testing
#[derive(Debug, Clone, PartialEq, Eq)]
enum PaymentStatus {
    Pending,
    Processing,
    Completed,
    Failed,
    Refunded,
    Cancelled,
}

impl PaymentStatus {
    fn as_str(&self) -> &'static str {
        match self {
            PaymentStatus::Pending => "pending",
            PaymentStatus::Processing => "processing",
            PaymentStatus::Completed => "completed",
            PaymentStatus::Failed => "failed",
            PaymentStatus::Refunded => "refunded",
            PaymentStatus::Cancelled => "cancelled",
        }
    }
}

impl FromStr for PaymentStatus {
    type Err = String;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "pending" => Ok(PaymentStatus::Pending),
            "processing" => Ok(PaymentStatus::Processing),
            "completed" => Ok(PaymentStatus::Completed),
            "failed" => Ok(PaymentStatus::Failed),
            "refunded" => Ok(PaymentStatus::Refunded),
            "cancelled" => Ok(PaymentStatus::Cancelled),
            _ => Err(format!("Invalid payment status: {}", s)),
        }
    }
}

#[derive(Debug, thiserror::Error)]
pub enum PaymentError {
    #[error("Invalid amount")]
    InvalidAmount,
    #[error("Invalid state transition")]
    InvalidStateTransition,
    #[error("Validation failed: {0}")]
    ValidationFailed(String),
}

// Generated code simulation (what macros would produce)
#[derive(Debug, Clone)]
struct GeneratedPayment {
    pub id: Uuid,
    pub amount: Decimal,
    pub status: PaymentStatus,
    pub user_id: Uuid,
}

impl GeneratedPayment {
    fn new(amount: Decimal, status: PaymentStatus, user_id: Uuid) -> Self {
        Self {
            id: Uuid::new_v4(),
            amount,
            status,
            user_id,
        }
    }

    // Generated by PaymentEntity macro - Level 0 pure functions
    fn mark_processing(&mut self) -> Result<(), PaymentError> {
        match self.status {
            PaymentStatus::Pending => {
                self.status = PaymentStatus::Processing;
                Ok(())
            }
            _ => Err(PaymentError::InvalidStateTransition),
        }
    }

    fn mark_completed(&mut self) -> Result<(), PaymentError> {
        match self.status {
            PaymentStatus::Processing => {
                self.status = PaymentStatus::Completed;
                Ok(())
            }
            _ => Err(PaymentError::InvalidStateTransition),
        }
    }

    fn total_amount(&self) -> Decimal {
        // Simulated tax calculation
        self.amount + (self.amount * Decimal::from_str("0.1").unwrap())
    }

    fn validate_amount(&self) -> Result<(), PaymentError> {
        if self.amount <= Decimal::ZERO {
            Err(PaymentError::InvalidAmount)
        } else {
            Ok(())
        }
    }

    fn is_refundable(&self) -> bool {
        matches!(self.status, PaymentStatus::Completed)
    }

    fn validate(&self) -> Result<(), PaymentError> {
        self.validate_amount()?;
        // Additional validations would go here
        Ok(())
    }

    fn can_transition_to(&self, new_status: PaymentStatus) -> bool {
        match (&self.status, new_status) {
            (PaymentStatus::Pending, PaymentStatus::Processing) => true,
            (PaymentStatus::Processing, PaymentStatus::Completed) => true,
            (PaymentStatus::Processing, PaymentStatus::Failed) => true,
            (PaymentStatus::Completed, PaymentStatus::Refunded) => true,
            _ => false,
        }
    }

    // Generated by Brazilian market features
    fn calculate_brazilian_tax(&self) -> Decimal {
        // Simulate ICMS, PIS, COFINS calculations
        let icms = self.amount * Decimal::from_str("0.18").unwrap(); // 18% ICMS
        let pis = self.amount * Decimal::from_str("0.0165").unwrap(); // 1.65% PIS
        let cofins = self.amount * Decimal::from_str("0.076").unwrap(); // 7.6% COFINS
        icms + pis + cofins
    }
}

// Manual implementation for comparison
#[derive(Debug, Clone)]
struct ManualPayment {
    pub id: Uuid,
    pub amount: Decimal,
    pub status: PaymentStatus,
    pub user_id: Uuid,
}

impl ManualPayment {
    fn new(amount: Decimal, status: PaymentStatus, user_id: Uuid) -> Self {
        Self {
            id: Uuid::new_v4(),
            amount,
            status,
            user_id,
        }
    }

    // Manual implementation - should be identical to generated
    fn mark_processing(&mut self) -> Result<(), PaymentError> {
        match self.status {
            PaymentStatus::Pending => {
                self.status = PaymentStatus::Processing;
                Ok(())
            }
            _ => Err(PaymentError::InvalidStateTransition),
        }
    }

    fn mark_completed(&mut self) -> Result<(), PaymentError> {
        match self.status {
            PaymentStatus::Processing => {
                self.status = PaymentStatus::Completed;
                Ok(())
            }
            _ => Err(PaymentError::InvalidStateTransition),
        }
    }

    fn total_amount(&self) -> Decimal {
        self.amount + (self.amount * Decimal::from_str("0.1").unwrap())
    }

    fn validate_amount(&self) -> Result<(), PaymentError> {
        if self.amount <= Decimal::ZERO {
            Err(PaymentError::InvalidAmount)
        } else {
            Ok(())
        }
    }

    fn is_refundable(&self) -> bool {
        matches!(self.status, PaymentStatus::Completed)
    }

    fn validate(&self) -> Result<(), PaymentError> {
        self.validate_amount()?;
        Ok(())
    }

    fn can_transition_to(&self, new_status: PaymentStatus) -> bool {
        match (&self.status, new_status) {
            (PaymentStatus::Pending, PaymentStatus::Processing) => true,
            (PaymentStatus::Processing, PaymentStatus::Completed) => true,
            (PaymentStatus::Processing, PaymentStatus::Failed) => true,
            (PaymentStatus::Completed, PaymentStatus::Refunded) => true,
            _ => false,
        }
    }

    fn calculate_brazilian_tax(&self) -> Decimal {
        let icms = self.amount * Decimal::from_str("0.18").unwrap();
        let pis = self.amount * Decimal::from_str("0.0165").unwrap();
        let cofins = self.amount * Decimal::from_str("0.076").unwrap();
        icms + pis + cofins
    }
}

// PIX payment performance tests
#[derive(Debug, Clone)]
struct GeneratedPixPayment {
    pub id: Uuid,
    pub payment_id: Uuid,
    pub pix_key: String,
    pub amount: Decimal,
    pub qr_code: String,
}

impl GeneratedPixPayment {
    fn new(payment_id: Uuid, pix_key: String, amount: Decimal) -> Self {
        Self {
            id: Uuid::new_v4(),
            payment_id,
            pix_key,
            amount,
            qr_code: String::new(),
        }
    }

    // Generated PIX validation - Brazilian market specific
    fn validate_pix_key(&self) -> Result<(), PaymentError> {
        if self.pix_key.contains("@") {
            // Email validation
            if self.pix_key.len() > 3 && self.pix_key.contains(".") {
                Ok(())
            } else {
                Err(PaymentError::ValidationFailed("Invalid email".to_string()))
            }
        } else if self.pix_key.len() == 11 {
            // CPF validation
            self.validate_cpf()
        } else if self.pix_key.len() == 14 {
            // CNPJ validation
            self.validate_cnpj()
        } else {
            Err(PaymentError::ValidationFailed("Invalid PIX key format".to_string()))
        }
    }

    fn validate_cpf(&self) -> Result<(), PaymentError> {
        // Simplified CPF validation for performance testing
        let digits: String = self.pix_key.chars().filter(|c| c.is_ascii_digit()).collect();
        if digits.len() == 11 {
            Ok(())
        } else {
            Err(PaymentError::ValidationFailed("Invalid CPF".to_string()))
        }
    }

    fn validate_cnpj(&self) -> Result<(), PaymentError> {
        // Simplified CNPJ validation for performance testing
        let digits: String = self.pix_key.chars().filter(|c| c.is_ascii_digit()).collect();
        if digits.len() == 14 {
            Ok(())
        } else {
            Err(PaymentError::ValidationFailed("Invalid CNPJ".to_string()))
        }
    }

    fn generate_qr_payload(&self) -> Result<String, PaymentError> {
        // Simplified PIX QR code generation
        Ok(format!(
            "PIX|{}|{}|BRL|{}",
            self.pix_key,
            self.amount,
            self.payment_id
        ))
    }

    fn is_expired(&self) -> bool {
        // For performance testing, always return false
        false
    }
}

#[derive(Debug, Clone)]
struct ManualPixPayment {
    pub id: Uuid,
    pub payment_id: Uuid,
    pub pix_key: String,
    pub amount: Decimal,
    pub qr_code: String,
}

impl ManualPixPayment {
    fn new(payment_id: Uuid, pix_key: String, amount: Decimal) -> Self {
        Self {
            id: Uuid::new_v4(),
            payment_id,
            pix_key,
            amount,
            qr_code: String::new(),
        }
    }

    // Manual PIX validation - should be identical to generated
    fn validate_pix_key(&self) -> Result<(), PaymentError> {
        if self.pix_key.contains("@") {
            if self.pix_key.len() > 3 && self.pix_key.contains(".") {
                Ok(())
            } else {
                Err(PaymentError::ValidationFailed("Invalid email".to_string()))
            }
        } else if self.pix_key.len() == 11 {
            self.validate_cpf()
        } else if self.pix_key.len() == 14 {
            self.validate_cnpj()
        } else {
            Err(PaymentError::ValidationFailed("Invalid PIX key format".to_string()))
        }
    }

    fn validate_cpf(&self) -> Result<(), PaymentError> {
        let digits: String = self.pix_key.chars().filter(|c| c.is_ascii_digit()).collect();
        if digits.len() == 11 {
            Ok(())
        } else {
            Err(PaymentError::ValidationFailed("Invalid CPF".to_string()))
        }
    }

    fn validate_cnpj(&self) -> Result<(), PaymentError> {
        let digits: String = self.pix_key.chars().filter(|c| c.is_ascii_digit()).collect();
        if digits.len() == 14 {
            Ok(())
        } else {
            Err(PaymentError::ValidationFailed("Invalid CNPJ".to_string()))
        }
    }

    fn generate_qr_payload(&self) -> Result<String, PaymentError> {
        Ok(format!(
            "PIX|{}|{}|BRL|{}",
            self.pix_key,
            self.amount,
            self.payment_id
        ))
    }

    fn is_expired(&self) -> bool {
        false
    }
}

// Benchmark functions
fn benchmark_payment_state_transitions(c: &mut Criterion) {
    let mut group = c.benchmark_group("payment_state_transitions");
    
    for amount in [100, 1000, 10000].iter() {
        let amount_decimal = Decimal::from(*amount);
        
        group.bench_with_input(
            BenchmarkId::new("generated_mark_processing", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let mut payment = GeneratedPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.mark_processing())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_mark_processing", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let mut payment = ManualPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.mark_processing())
                })
            },
        );
    }
    
    group.finish();
}

fn benchmark_payment_validation(c: &mut Criterion) {
    let mut group = c.benchmark_group("payment_validation");
    
    let amounts = vec![
        Decimal::from_str("0.01").unwrap(),
        Decimal::from_str("100.50").unwrap(),
        Decimal::from_str("10000.99").unwrap(),
    ];

    for (i, amount) in amounts.iter().enumerate() {
        group.bench_with_input(
            BenchmarkId::new("generated_validate", i),
            amount,
            |b, &amount| {
                b.iter(|| {
                    let payment = GeneratedPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.validate())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_validate", i),
            amount,
            |b, &amount| {
                b.iter(|| {
                    let payment = ManualPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.validate())
                })
            },
        );
    }
    
    group.finish();
}

fn benchmark_payment_calculations(c: &mut Criterion) {
    let mut group = c.benchmark_group("payment_calculations");
    
    for amount in [100, 1000, 10000].iter() {
        let amount_decimal = Decimal::from(*amount);
        
        group.bench_with_input(
            BenchmarkId::new("generated_total_amount", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let payment = GeneratedPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.total_amount())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_total_amount", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let payment = ManualPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.total_amount())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("generated_brazilian_tax", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let payment = GeneratedPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.calculate_brazilian_tax())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_brazilian_tax", amount),
            &amount_decimal,
            |b, &amount| {
                b.iter(|| {
                    let payment = ManualPayment::new(
                        amount,
                        PaymentStatus::Pending,
                        Uuid::new_v4(),
                    );
                    black_box(payment.calculate_brazilian_tax())
                })
            },
        );
    }
    
    group.finish();
}

fn benchmark_pix_validation(c: &mut Criterion) {
    let mut group = c.benchmark_group("pix_validation");
    
    let test_cases = vec![
        ("email", "user@example.com"),
        ("cpf", "12345678901"),
        ("cnpj", "12345678000190"),
        ("invalid", "invalid_key"),
    ];

    for (case_name, pix_key) in test_cases.iter() {
        group.bench_with_input(
            BenchmarkId::new("generated_pix_validation", case_name),
            pix_key,
            |b, &pix_key| {
                b.iter(|| {
                    let pix = GeneratedPixPayment::new(
                        Uuid::new_v4(),
                        pix_key.to_string(),
                        Decimal::from(100),
                    );
                    black_box(pix.validate_pix_key())
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_pix_validation", case_name),
            pix_key,
            |b, &pix_key| {
                b.iter(|| {
                    let pix = ManualPixPayment::new(
                        Uuid::new_v4(),
                        pix_key.to_string(),
                        Decimal::from(100),
                    );
                    black_box(pix.validate_pix_key())
                })
            },
        );
    }
    
    group.finish();
}

fn benchmark_pix_qr_generation(c: &mut Criterion) {
    let mut group = c.benchmark_group("pix_qr_generation");
    
    let pix_key = "user@example.com";
    let amount = Decimal::from(100);

    group.bench_function("generated_qr_payload", |b| {
        b.iter(|| {
            let pix = GeneratedPixPayment::new(
                Uuid::new_v4(),
                pix_key.to_string(),
                amount,
            );
            black_box(pix.generate_qr_payload())
        })
    });

    group.bench_function("manual_qr_payload", |b| {
        b.iter(|| {
            let pix = ManualPixPayment::new(
                Uuid::new_v4(),
                pix_key.to_string(),
                amount,
            );
            black_box(pix.generate_qr_payload())
        })
    });
    
    group.finish();
}

fn benchmark_bulk_operations(c: &mut Criterion) {
    let mut group = c.benchmark_group("bulk_operations");
    
    let batch_sizes = vec![100, 1000, 5000];

    for batch_size in batch_sizes.iter() {
        group.bench_with_input(
            BenchmarkId::new("generated_bulk_validation", batch_size),
            batch_size,
            |b, &size| {
                b.iter(|| {
                    let payments: Vec<_> = (0..size)
                        .map(|i| {
                            GeneratedPayment::new(
                                Decimal::from(i as i64 + 1),
                                PaymentStatus::Pending,
                                Uuid::new_v4(),
                            )
                        })
                        .collect();

                    let results: Result<Vec<_>, _> = payments
                        .iter()
                        .map(|p| p.validate())
                        .collect();
                    
                    black_box(results)
                })
            },
        );

        group.bench_with_input(
            BenchmarkId::new("manual_bulk_validation", batch_size),
            batch_size,
            |b, &size| {
                b.iter(|| {
                    let payments: Vec<_> = (0..size)
                        .map(|i| {
                            ManualPayment::new(
                                Decimal::from(i as i64 + 1),
                                PaymentStatus::Pending,
                                Uuid::new_v4(),
                            )
                        })
                        .collect();

                    let results: Result<Vec<_>, _> = payments
                        .iter()
                        .map(|p| p.validate())
                        .collect();
                    
                    black_box(results)
                })
            },
        );
    }
    
    group.finish();
}

criterion_group!(
    benches,
    benchmark_payment_state_transitions,
    benchmark_payment_validation,
    benchmark_payment_calculations,
    benchmark_pix_validation,
    benchmark_pix_qr_generation,
    benchmark_bulk_operations
);
criterion_main!(benches);