use super::*;

//= spec/implementation.md#i-o-requirements
//= type=test
//# `RING-IMPL-IO-001` The Borromean backing abstraction MUST expose only
//# the primitive operations needed to satisfy
//# [spec/ring/00-introduction.md](ring/00-introduction.md): region or metadata reads, writes,
//# erases, and durability barriers.
#[test]
fn requirement_flash_io_trait_exposes_only_primitive_storage_operations() {
    struct SurfaceCheckedFlash;

    impl FlashIo for SurfaceCheckedFlash {
        fn read_metadata(&mut self) -> Result<Option<StorageMetadata>, StorageIoError> {
            Ok(None)
        }

        fn write_metadata(&mut self, _metadata: StorageMetadata) -> Result<(), StorageIoError> {
            Ok(())
        }

        fn read_region<R, F>(
            &mut self,
            _region_index: u32,
            _offset: usize,
            _len: usize,
            read: F,
        ) -> Result<R, StorageIoError>
        where
            F: FnOnce(&[u8]) -> R,
        {
            Ok(read(&[]))
        }

        fn write_region(
            &mut self,
            _region_index: u32,
            _offset: usize,
            _data: &[u8],
        ) -> Result<(), StorageIoError> {
            Ok(())
        }

        fn erase_region(&mut self, _region_index: u32) -> Result<(), StorageIoError> {
            Ok(())
        }

        fn sync(&mut self) -> Result<(), StorageIoError> {
            Ok(())
        }

        fn format_empty_store(
            &mut self,
            _min_free_regions: u32,
            _wal_write_granule: u32,
            _wal_record_magic: u8,
        ) -> Result<StorageMetadata, StorageFormatError> {
            Err(StorageFormatError::from(
                MockFormatError::RegionCountTooLarge,
            ))
        }
    }

    // This is a compile-time surface test. The impl above names exactly
    // the primitive operations allowed by the requirement. If `FlashIo`
    // gained another required method, or if any of these signatures
    // stopped exposing the expected `Result`-based contract, this test
    // would stop compiling. Runtime-style hook surfaces are separately
    // prohibited by `RING-IMPL-IO-005`.
    let _: fn(&mut SurfaceCheckedFlash) -> Result<Option<StorageMetadata>, StorageIoError> =
        <SurfaceCheckedFlash as FlashIo>::read_metadata;
    let _: fn(&mut SurfaceCheckedFlash, StorageMetadata) -> Result<(), StorageIoError> =
        <SurfaceCheckedFlash as FlashIo>::write_metadata;
    type ReadRegionFn = fn(
        &mut SurfaceCheckedFlash,
        u32,
        usize,
        usize,
        fn(&[u8]) -> usize,
    ) -> Result<usize, StorageIoError>;

    let _: ReadRegionFn =
        <SurfaceCheckedFlash as FlashIo>::read_region::<usize, fn(&[u8]) -> usize>;
    let _: fn(&mut SurfaceCheckedFlash, u32, usize, &[u8]) -> Result<(), StorageIoError> =
        <SurfaceCheckedFlash as FlashIo>::write_region;
    let _: fn(&mut SurfaceCheckedFlash, u32) -> Result<(), StorageIoError> =
        <SurfaceCheckedFlash as FlashIo>::erase_region;
    let _: fn(&mut SurfaceCheckedFlash) -> Result<(), StorageIoError> =
        <SurfaceCheckedFlash as FlashIo>::sync;
    let _: fn(
        &mut SurfaceCheckedFlash,
        u32,
        u32,
        u8,
    ) -> Result<StorageMetadata, StorageFormatError> =
        <SurfaceCheckedFlash as FlashIo>::format_empty_store;
}

//= spec/implementation.md#i-o-requirements
//= type=test
//# `RING-IMPL-IO-002` The Borromean backing abstraction MUST be generic
//# over the caller's concrete transport, flash driver, emulator, or
//# synchronization wrapper type.
#[test]
fn requirement_flash_io_trait_accepts_caller_defined_driver_types() {
    const REGION_SIZE: usize = 256;
    const REGION_COUNT: usize = 5;
    let mut flash = ForwardingFlash::<REGION_SIZE, REGION_COUNT, 2048>::new(0xff);
    let mut workspace = StorageWorkspace::<REGION_SIZE>::new();
    let mut storage = Storage::<_, REGION_SIZE, REGION_COUNT, 8>::format(
        &mut flash,
        StorageFormatConfig::new(1, 8, 0xa5),
        crate::test_storage_memory(),
    )
    .unwrap();
    storage.create_map(CollectionId(62)).unwrap();
    assert_eq!(storage.collections()[0].collection_id(), CollectionId(62));
}

//= spec/implementation.md#i-o-requirements
//= type=test
//# `RING-IMPL-IO-003` The Borromean backing abstraction MUST be usable
//# without dynamic dispatch and without heap allocation.
#[test]
fn requirement_flash_io_trait_supports_non_allocating_concrete_driver_usage() {
    const REGION_SIZE: usize = 256;
    const REGION_COUNT: usize = 5;

    let mut storage_memory = StorageMemory::<REGION_SIZE, REGION_COUNT, 8>::new();
    assert_no_alloc("FlashIo concrete-driver operation path", || {
        let mut flash = ForwardingFlash::<REGION_SIZE, REGION_COUNT, 2048>::new(0xff);
        let mut workspace = StorageWorkspace::<REGION_SIZE>::new();
        let mut storage = Storage::<_, REGION_SIZE, REGION_COUNT, 8>::format(
            &mut flash,
            StorageFormatConfig::new(1, 8, 0xa5),
            &mut storage_memory,
        )
        .unwrap();
        storage.create_map(CollectionId(63)).unwrap();
        assert_eq!(storage.collections()[0].collection_id(), CollectionId(63));
    });
}

//= spec/implementation.md#i-o-requirements
//= type=test
//# `RING-IMPL-IO-004` If the target medium does not require an
//# explicit durability barrier, the backing abstraction MAY implement sync
//# as a zero-cost completed operation.
#[test]
fn requirement_mock_flash_sync_can_complete_immediately() {
    let mut flash = MockFlash::<128, 4, 8>::new(0xff);
    flash.clear_operations();
    flash.sync().unwrap();
    assert_eq!(flash.operations(), &[MockOperation::Sync]);
}

//= spec/implementation.md#i-o-requirements
//= type=test
//# `RING-IMPL-IO-005` Borromean MUST treat wakeups, DMA completion, or
//# interrupt delivery as an external concern of the caller-provided backing
//# implementation rather than as an internal runtime service.
#[test]
fn requirement_flash_io_runtime_hook_policy_is_enforced_by_clippy_verification() {
    // The mechanical enforcement for this requirement lives in
    // `clippy.toml`, the crate-level deny configuration in `src/lib.rs`,
    // and the lib-only clippy policy pass in `scripts/verify.sh`.
}
