#[path = "../src/activity.rs"]
mod activity;
#[path = "../src/models.rs"]
mod models;
#[path = "../src/storage.rs"]
mod storage;

use models::{
    ActivityEntry, ActivityLevel, ComponentId, ComponentState, ComponentStatus, LocalSingBoxConfig,
    Profile, ProfileItem, ProfileItemType, Protocol, ProxyProtocol, SubscriptionCache,
    SubscriptionServer, Target, TargetKind,
};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};
use storage::{backup_path, default_config_root, JsonStorage, StoragePaths};

#[test]
fn storage_defaults_to_programdata_root() {
    let expected = PathBuf::from(r"C:\ProgramData\VpnProxy");

    assert_eq!(default_config_root(), expected);
    assert_eq!(StoragePaths::default().root, expected);
}

#[test]
fn roundtrips_profiles_targets_components_and_activity() {
    let root = test_root("roundtrip");
    let storage = JsonStorage::new(root.clone());

    let profiles = vec![sample_profile("discord")];
    let targets = vec![sample_target("home-gateway")];
    let components = vec![sample_component()];
    let activity = vec![sample_activity(
        "created",
        "2026-01-01T10:00:00Z",
        ActivityLevel::Success,
    )];

    storage.write_profiles(&profiles).expect("write profiles");
    storage.write_targets(&targets).expect("write targets");
    storage
        .write_components(&components)
        .expect("write components");
    storage.write_activity(&activity).expect("write activity");

    assert_eq!(storage.read_profiles().expect("read profiles"), profiles);
    assert_eq!(storage.read_targets().expect("read targets"), targets);
    assert_eq!(
        storage.read_components().expect("read components"),
        components
    );
    assert_eq!(storage.read_activity().expect("read activity"), activity);

    cleanup(&root);
}

#[test]
fn roundtrips_local_singbox_config_and_subscription_cache() {
    let root = test_root("local-singbox");
    let storage = JsonStorage::new(root.clone());
    let config = LocalSingBoxConfig {
        subscription_url: Some("https://sub.example.test/path?token=secret".to_string()),
        selected_server_tag: Some("nl-1".to_string()),
        listen_host: "127.0.0.1".to_string(),
        listen_port: 1080,
        service_name: "VpnProxySingBox".to_string(),
        install_root: r"C:\Program Files\VpnProxy\sing-box".to_string(),
        updated_at: Some("2026-07-07T10:00:00Z".to_string()),
    };
    let cache = sample_subscription_cache();

    storage
        .write_local_singbox_config(&config)
        .expect("write local sing-box config");
    storage
        .write_singbox_subscription_cache(&cache)
        .expect("write subscription cache");

    assert_eq!(
        storage
            .read_local_singbox_config()
            .expect("read local sing-box config"),
        config
    );
    assert_eq!(
        storage
            .read_singbox_subscription_cache()
            .expect("read subscription cache"),
        Some(cache)
    );
    assert_eq!(
        config.subscription_display_url(),
        Some("https://sub.example.test/...".to_string())
    );

    cleanup(&root);
}

#[test]
fn missing_local_singbox_config_defaults_to_optional_empty_state() {
    let root = test_root("local-singbox-default");
    let storage = JsonStorage::new(root.clone());
    let config = storage
        .read_local_singbox_config()
        .expect("read default local sing-box config");

    assert_eq!(config.subscription_url, None);
    assert_eq!(config.selected_server_tag, None);
    assert_eq!(config.listen_host, "127.0.0.1");
    assert_eq!(config.listen_port, 1080);
    assert_eq!(config.service_name, "VpnProxySingBox");

    cleanup(&root);
}

#[test]
fn invalid_subscription_cache_falls_back_to_none() {
    let root = test_root("invalid-subscription-cache");
    let storage = JsonStorage::new(root.clone());
    storage.ensure_dirs().expect("create storage dirs");
    fs::write(
        &storage.paths().singbox_subscription_cache_file,
        "{not valid json",
    )
    .expect("write invalid cache");

    assert_eq!(
        storage
            .read_singbox_subscription_cache()
            .expect("invalid cache fallback"),
        None
    );

    cleanup(&root);
}

#[test]
fn invalid_json_falls_back_to_empty_collection() {
    let root = test_root("invalid-json");
    let storage = JsonStorage::new(root.clone());
    storage.ensure_dirs().expect("create storage dirs");
    fs::write(&storage.paths().profiles_file, "{not valid json").expect("write invalid json");

    assert_eq!(
        storage.read_profiles().expect("invalid profiles fallback"),
        Vec::<Profile>::new()
    );

    cleanup(&root);
}

#[test]
fn write_creates_backup_before_overwriting_source_file() {
    let root = test_root("backup");
    let storage = JsonStorage::new(root.clone());
    let first = vec![sample_profile("first")];
    let second = vec![sample_profile("second")];

    storage.write_profiles(&first).expect("first write");
    storage.write_profiles(&second).expect("second write");

    let backup = backup_path(&storage.paths().profiles_file);
    assert!(backup.exists(), "backup file should exist");

    let backup_contents = fs::read_to_string(backup).expect("read backup");
    let backup_profiles: Vec<Profile> =
        serde_json::from_str(&backup_contents).expect("backup json");

    assert_eq!(backup_profiles, first);
    assert_eq!(storage.read_profiles().expect("current profiles"), second);

    cleanup(&root);
}

#[test]
fn activity_entries_are_sorted_and_capped() {
    let root = test_root("activity");
    let storage = JsonStorage::with_activity_limit(root.clone(), 2);

    storage
        .append_activity(sample_activity(
            "old",
            "2026-01-01T10:00:00Z",
            ActivityLevel::Info,
        ))
        .expect("append old");
    storage
        .append_activity(sample_activity(
            "new",
            "2026-01-03T10:00:00Z",
            ActivityLevel::Success,
        ))
        .expect("append new");
    storage
        .append_activity(sample_activity(
            "middle",
            "2026-01-02T10:00:00Z",
            ActivityLevel::Warning,
        ))
        .expect("append middle");

    let entries = storage.read_activity().expect("read capped activity");

    assert_eq!(entries.len(), 2);
    assert_eq!(
        entries
            .iter()
            .map(|entry| entry.id.as_str())
            .collect::<Vec<_>>(),
        vec!["new", "middle"]
    );

    cleanup(&root);
}

fn test_root(name: &str) -> PathBuf {
    let timestamp = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system clock before unix epoch")
        .as_nanos();

    std::env::temp_dir().join(format!("vpn-proxy-storage-{name}-{timestamp}"))
}

fn cleanup(root: &Path) {
    let _ = fs::remove_dir_all(root);
}

fn sample_profile(id: &str) -> Profile {
    Profile {
        id: id.to_string(),
        name: format!("Profile {id}"),
        enabled: true,
        target_id: "home-gateway".to_string(),
        protocols: vec![Protocol::Tcp, Protocol::Udp],
        items: vec![ProfileItem {
            item_type: ProfileItemType::Process,
            value: "Discord".to_string(),
            recursive: false,
        }],
    }
}

fn sample_target(id: &str) -> Target {
    Target {
        id: id.to_string(),
        name: "Home Gateway".to_string(),
        kind: TargetKind::External,
        protocol: ProxyProtocol::Socks5,
        host: "192.168.50.111".to_string(),
        port: 8080,
        requires_component: None,
    }
}

fn sample_component() -> ComponentStatus {
    ComponentStatus {
        id: ComponentId::Proxyfier,
        name: "ProxiFyre".to_string(),
        state: ComponentState::Missing,
        installed: false,
        running: false,
        version: None,
        path: None,
        problems: vec!["ProxiFyre не установлен".to_string()],
        actions: vec!["Установить ProxiFyre".to_string()],
    }
}

fn sample_subscription_cache() -> SubscriptionCache {
    SubscriptionCache {
        config: serde_json::json!({
            "outbounds": [
                {
                    "type": "vless",
                    "tag": "nl-1",
                    "server": "nl.example.test",
                    "server_port": 443
                }
            ]
        }),
        servers: vec![SubscriptionServer {
            tag: "nl-1".to_string(),
            server_type: "vless".to_string(),
            server: "nl.example.test".to_string(),
            server_port: 443,
        }],
        user_info: serde_json::Map::new(),
        fetched_at: "2026-07-07T10:00:00Z".to_string(),
    }
}

fn sample_activity(id: &str, at: &str, level: ActivityLevel) -> ActivityEntry {
    ActivityEntry {
        id: id.to_string(),
        at: at.to_string(),
        level,
        title: format!("Activity {id}"),
        message: "Storage test activity".to_string(),
    }
}
