//! Tests for transaction token system macros.

use std::io::{Error as IoError, ErrorKind};

// Generate error enums from concept map
mad_hatter::transaction_errors!("concept-map.json");

// ── transaction_errors! tests ──

#[test]
fn test_beat_cycle_error_enum_exists() {
    // BeatCycleError should have variants only for fatal operations
    let err = BeatCycleError::AutoReadExecute(
        Box::new(IoError::new(ErrorKind::NotFound, "test"))
    );
    assert!(format!("{}", err).contains("auto_read_execute failed"));
}

#[test]
fn test_beat_cycle_error_variants() {
    // All fatal variants should exist
    let _ = BeatCycleError::AutoReadExecute(Box::new(IoError::new(ErrorKind::Other, "a")));
    let _ = BeatCycleError::BuildPrompt(Box::new(IoError::new(ErrorKind::Other, "b")));
    let _ = BeatCycleError::LlmInference(Box::new(IoError::new(ErrorKind::Other, "c")));
    // accept/suppress variants should NOT exist (compile error if uncommented):
    // let _ = BeatCycleError::AutoReadInsertLog(...);  // accept → not in enum
    // let _ = BeatCycleError::LoadSettings(...);       // suppress → not in enum
}

#[test]
fn test_beat_cycle_error_display() {
    let err = BeatCycleError::BuildPrompt(
        Box::new(IoError::new(ErrorKind::Other, "prompt too large"))
    );
    let msg = format!("{}", err);
    assert!(msg.contains("beat_cycle::build_prompt failed"));
    assert!(msg.contains("prompt too large"));
}

#[test]
fn test_beat_cycle_error_source() {
    use std::error::Error;
    let inner = IoError::new(ErrorKind::Other, "inner error");
    let err = BeatCycleError::LlmInference(Box::new(inner));
    assert!(err.source().is_some());
}

// ── io! macro tests ──

#[test]
fn test_io_success() {
    fn do_work() -> Result<i32, BeatCycleError> {
        let value = mad_hatter::io!(beat_cycle::auto_read_execute, {
            Ok::<i32, IoError>(42)
        })?;
        Ok(value)
    }
    assert_eq!(do_work().unwrap(), 42);
}

#[test]
fn test_io_error_conversion() {
    fn do_work() -> Result<i32, BeatCycleError> {
        let value = mad_hatter::io!(beat_cycle::build_prompt, {
            Err::<i32, IoError>(IoError::new(ErrorKind::Other, "build failed"))
        })?;
        Ok(value)
    }
    let err = do_work().unwrap_err();
    assert!(matches!(err, BeatCycleError::BuildPrompt(_)));
    assert!(format!("{}", err).contains("build failed"));
}

// ── accept! macro tests ──

#[test]
fn test_accept_success() {
    let value = mad_hatter::accept!(beat_cycle::auto_read_insert_log, {
        Ok::<i32, IoError>(99)
    }, {
        0  // fallback value
    });
    assert_eq!(value, 99);
}

#[test]
fn test_accept_fallback() {
    let value = mad_hatter::accept!(beat_cycle::auto_read_insert_log, {
        Err::<i32, IoError>(IoError::new(ErrorKind::Other, "insert failed"))
    }, {
        -1  // fallback value
    });
    assert_eq!(value, -1);
}

#[test]
fn test_accept_early_return() {
    fn do_work() -> Result<i32, BeatCycleError> {
        mad_hatter::accept!(beat_cycle::action_execute, {
            Err::<(), IoError>(IoError::new(ErrorKind::Other, "action failed"))
        }, {
            return Ok(-1);  // early return from function
        });
        Ok(42)
    }
    // Should return -1 (early return from fallback), not 42
    assert_eq!(do_work().unwrap(), -1);
}

// ── suppress! macro tests ──

#[test]
fn test_suppress_success() {
    let value = mad_hatter::suppress!(beat_cycle::load_settings, {
        Ok::<String, IoError>("settings".to_string())
    }, "settings are optional");
    assert_eq!(value, Some("settings".to_string()));
}

#[test]
fn test_suppress_returns_none() {
    let value = mad_hatter::suppress!(beat_cycle::record_thinking, {
        Err::<String, IoError>(IoError::new(ErrorKind::Other, "write failed"))
    }, "thinking note is best-effort");
    assert_eq!(value, None);
}

// ── #[transaction] attribute test ──

#[mad_hatter::transaction(beat_cycle)]
fn example_transaction_fn() -> Result<(), BeatCycleError> {
    let _ = mad_hatter::io!(beat_cycle::auto_read_execute, {
        Ok::<(), IoError>(())
    })?;
    Ok(())
}

#[test]
fn test_transaction_attribute() {
    assert!(example_transaction_fn().is_ok());
}