//! JSON-file implementation of the [`WindowStateStore`] port.
//!
//! The snapshot is machine-local and lives under the app-data directory:
//! `<app_data_dir>/windows.json`.

use std::sync::Arc;

use async_trait::async_trait;

use domain::layout::WindowStateSnapshot;
use domain::ports::{FileSystem, RemotePath, StoreError, WindowStateStore};

/// File name of the persisted OS window-state snapshot.
const WINDOWS_FILE: &str = "windows.json";

/// JSON-file implementation of [`WindowStateStore`].
#[derive(Clone)]
pub struct FsWindowStateStore {
    fs: Arc<dyn FileSystem>,
    app_data_dir: String,
}

impl FsWindowStateStore {
    /// Builds the store from an injected filesystem and app-data directory.
    #[must_use]
    pub fn new(fs: Arc<dyn FileSystem>, app_data_dir: impl Into<String>) -> Self {
        Self {
            fs,
            app_data_dir: app_data_dir.into(),
        }
    }

    fn path(&self) -> RemotePath {
        let base = self.app_data_dir.trim_end_matches(['/', '\\']);
        RemotePath::new(format!("{base}/{WINDOWS_FILE}"))
    }

    async fn ensure_dir(&self) -> Result<(), StoreError> {
        let dir = RemotePath::new(self.app_data_dir.trim_end_matches(['/', '\\']).to_owned());
        self.fs
            .create_dir_all(&dir)
            .await
            .map_err(|e| StoreError::Io(e.to_string()))
    }
}

#[async_trait]
impl WindowStateStore for FsWindowStateStore {
    async fn save_window_state(&self, snapshot: &WindowStateSnapshot) -> Result<(), StoreError> {
        self.ensure_dir().await?;
        let bytes = serde_json::to_vec_pretty(snapshot)
            .map_err(|e| StoreError::Serialization(e.to_string()))?;
        self.fs
            .write(&self.path(), &bytes)
            .await
            .map_err(|e| StoreError::Io(e.to_string()))
    }

    async fn load_window_state(&self) -> Result<WindowStateSnapshot, StoreError> {
        match self.fs.read(&self.path()).await {
            Ok(bytes) => {
                serde_json::from_slice(&bytes).map_err(|e| StoreError::Serialization(e.to_string()))
            }
            Err(domain::ports::FsError::NotFound(_)) => Ok(WindowStateSnapshot::default()),
            Err(e) => Err(StoreError::Io(e.to_string())),
        }
    }
}
