use proxywarden_lib::adapters::proxifyre::{
    ProxiFyreAdapter, ProxiFyreConfig, PROXIFYRE_OUTPUT_FILE,
};
use proxywarden_lib::adapters::proxy_router::{
    ProxyRouterAdapter, ProxyRouterErrorKind, ProxyRouterRequest,
};
use proxywarden_lib::models::{
    ComponentId, ComponentState, ComponentStatus, Profile, ProfileItem, ProfileItemType, Protocol,
    ProxyProtocol, Target, TargetKind,
};

#[test]
fn generates_proxifyre_config_for_discord_external_socks5_target() {
    let adapter = ProxiFyreAdapter::default();
    let profiles = vec![discord_profile("home-gateway")];
    let targets = vec![external_socks5_target()];
    let components = vec![missing_singbox_component()];

    let generated = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect("external socks5 target should not require sing-box");
    let config: ProxiFyreConfig =
        serde_json::from_str(&generated.contents).expect("generated config json");

    assert_eq!(generated.adapter_id, "proxifyre");
    assert_eq!(generated.output_file_name, PROXIFYRE_OUTPUT_FILE);
    assert_eq!(generated.enabled_profiles, 1);
    assert_eq!(generated.routed_apps, 1);
    assert_eq!(config.log_level, "Info");
    assert!(config.bypass_lan);
    assert_eq!(config.proxies.len(), 1);
    assert_eq!(config.proxies[0].app_names, vec!["Discord"]);
    assert_eq!(
        config.proxies[0].socks5_proxy_endpoint,
        "192.168.50.111:8080"
    );
    assert_eq!(config.proxies[0].supported_protocols, vec!["TCP", "UDP"]);
}

#[test]
fn skips_disabled_profiles_when_generating_proxifyre_config() {
    let adapter = ProxiFyreAdapter::default();
    let mut disabled = discord_profile("home-gateway");
    disabled.enabled = false;
    let profiles = vec![disabled];
    let targets = vec![external_socks5_target()];
    let components = Vec::new();

    let generated = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect("disabled profiles should produce empty config");
    let config: ProxiFyreConfig =
        serde_json::from_str(&generated.contents).expect("generated config json");

    assert_eq!(generated.enabled_profiles, 0);
    assert_eq!(generated.routed_apps, 0);
    assert!(config.proxies.is_empty());
}

#[test]
fn includes_folder_paths_when_generating_proxifyre_config() {
    let adapter = ProxiFyreAdapter::default();
    let mut profile = discord_profile("home-gateway");
    profile.items.push(ProfileItem {
        item_type: ProfileItemType::Folder,
        value: r"C:\Games\MyGame".to_string(),
        recursive: true,
    });
    let profiles = vec![profile];
    let targets = vec![external_socks5_target()];
    let components = Vec::new();

    let generated = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect("folder paths should be accepted by ProxiFyre config generation");
    let config: ProxiFyreConfig =
        serde_json::from_str(&generated.contents).expect("generated config json");

    assert_eq!(generated.routed_apps, 2);
    assert_eq!(
        config.proxies[0].app_names,
        vec!["Discord", r"C:\Games\MyGame"]
    );
}

#[test]
fn blocks_local_singbox_target_when_required_component_is_missing() {
    let adapter = ProxiFyreAdapter::default();
    let profiles = vec![discord_profile("local-singbox")];
    let targets = vec![local_singbox_target()];
    let components = Vec::new();

    let error = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect_err("local sing-box target should require installed running sing-box");

    assert_eq!(error.kind, ProxyRouterErrorKind::MissingRequiredComponent);
}

#[test]
fn local_singbox_target_generates_when_required_component_is_running() {
    let adapter = ProxiFyreAdapter::default();
    let profiles = vec![discord_profile("local-singbox")];
    let targets = vec![local_singbox_target()];
    let components = vec![running_singbox_component()];

    let generated = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect("running sing-box should satisfy local target dependency");
    let config: ProxiFyreConfig =
        serde_json::from_str(&generated.contents).expect("generated config json");

    assert_eq!(config.proxies.len(), 1);
    assert_eq!(config.proxies[0].socks5_proxy_endpoint, "127.0.0.1:1080");
}

#[test]
fn rejects_http_target_because_proxifyre_adapter_is_socks5_only() {
    let adapter = ProxiFyreAdapter::default();
    let profiles = vec![discord_profile("office-http")];
    let targets = vec![Target {
        id: "office-http".to_string(),
        name: "Office HTTP".to_string(),
        kind: TargetKind::External,
        protocol: ProxyProtocol::Http,
        host: "192.168.50.111".to_string(),
        port: 3128,
        requires_component: None,
    }];
    let components = Vec::new();

    let error = adapter
        .generate_config(ProxyRouterRequest::new(&profiles, &targets, &components))
        .expect_err("ProxiFyre should reject HTTP targets");

    assert_eq!(error.kind, ProxyRouterErrorKind::UnsupportedTargetProtocol);
}

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

fn external_socks5_target() -> Target {
    Target {
        id: "home-gateway".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 local_singbox_target() -> Target {
    Target {
        id: "local-singbox".to_string(),
        name: "Local sing-box".to_string(),
        kind: TargetKind::Local,
        protocol: ProxyProtocol::Socks5,
        host: "127.0.0.1".to_string(),
        port: 1080,
        requires_component: Some(ComponentId::Singbox),
    }
}

fn missing_singbox_component() -> ComponentStatus {
    ComponentStatus {
        id: ComponentId::Singbox,
        name: "Local sing-box".to_string(),
        state: ComponentState::Missing,
        installed: false,
        running: false,
        version: None,
        path: None,
        problems: vec!["Local sing-box is not installed".to_string()],
        actions: vec!["Install Local sing-box".to_string()],
    }
}

fn running_singbox_component() -> ComponentStatus {
    ComponentStatus {
        id: ComponentId::Singbox,
        name: "Local sing-box".to_string(),
        state: ComponentState::Running,
        installed: true,
        running: true,
        version: Some("1.11.0".to_string()),
        path: Some(r"C:\Tools\ProxyWarden\sing-box\sing-box.exe".to_string()),
        problems: Vec::new(),
        actions: vec!["Restart".to_string(), "Stop".to_string()],
    }
}
