//! L1 integration tests for [`LocalFileSystem`] against a real temp directory.

use std::path::PathBuf;

use domain::ports::{FileSystem, FsError, RemotePath};
use infrastructure::LocalFileSystem;
use uuid::Uuid;

/// A unique scratch directory under the OS temp dir, cleaned up on drop.
struct TempDir(PathBuf);
impl TempDir {
    fn new() -> Self {
        let p = std::env::temp_dir().join(format!("idea-l1-{}", Uuid::new_v4()));
        std::fs::create_dir_all(&p).unwrap();
        Self(p)
    }
    fn child(&self, name: &str) -> RemotePath {
        RemotePath::new(self.0.join(name).to_string_lossy().into_owned())
    }
    fn path(&self) -> RemotePath {
        RemotePath::new(self.0.to_string_lossy().into_owned())
    }
}
impl Drop for TempDir {
    fn drop(&mut self) {
        let _ = std::fs::remove_dir_all(&self.0);
    }
}

#[tokio::test]
async fn write_then_read_roundtrips() {
    let tmp = TempDir::new();
    let fs = LocalFileSystem::new();
    let file = tmp.child("hello.txt");

    fs.write(&file, b"bonjour").await.unwrap();
    let back = fs.read(&file).await.unwrap();
    assert_eq!(back, b"bonjour");
}

#[tokio::test]
async fn exists_reflects_presence() {
    let tmp = TempDir::new();
    let fs = LocalFileSystem::new();
    let file = tmp.child("maybe.txt");

    assert!(!fs.exists(&file).await.unwrap());
    fs.write(&file, b"x").await.unwrap();
    assert!(fs.exists(&file).await.unwrap());
}

#[tokio::test]
async fn create_dir_all_and_list() {
    let tmp = TempDir::new();
    let fs = LocalFileSystem::new();

    let nested = tmp.child("a/b/c");
    fs.create_dir_all(&nested).await.unwrap();
    assert!(fs.exists(&nested).await.unwrap());

    // Put a file and a dir at the top level, then list them.
    fs.write(&tmp.child("file.txt"), b"y").await.unwrap();
    let entries = fs.list(&tmp.path()).await.unwrap();

    let names: Vec<&str> = entries.iter().map(|e| e.name.as_str()).collect();
    assert!(names.contains(&"file.txt"));
    assert!(names.contains(&"a"));

    let dir_entry = entries.iter().find(|e| e.name == "a").unwrap();
    assert!(dir_entry.is_dir);
    let file_entry = entries.iter().find(|e| e.name == "file.txt").unwrap();
    assert!(!file_entry.is_dir);
}

#[tokio::test]
async fn read_missing_maps_to_not_found() {
    let tmp = TempDir::new();
    let fs = LocalFileSystem::new();
    let err = fs.read(&tmp.child("nope.txt")).await.unwrap_err();
    assert!(matches!(err, FsError::NotFound(_)), "got {err:?}");
}
