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

use helper::{
    helper_action_requires_elevation, install_request, parse_helper_response,
    proxifyre_apply_request, service_request, HelperAction, HelperCommandOutput,
    HelperCommandRunner, HelperCommandSpec, HelperError, HelperResponse, StructuredHelper,
};
use models::ComponentId;
use serde_json::json;
use std::cell::RefCell;
use std::path::PathBuf;

#[test]
fn structured_helper_serializes_request_and_parses_json_response() {
    let runner = MockRunner {
        output: HelperCommandOutput {
            status_code: 0,
            stdout: serde_json::to_string(&HelperResponse {
                success: true,
                action: HelperAction::ProxyfierApply,
                changed: true,
                message: "Applied".to_string(),
                details: json!({ "serviceName": "ProxiFyreService" }),
            })
            .expect("response json"),
            stderr: String::new(),
        },
        seen: RefCell::new(Vec::new()),
    };
    let helper = StructuredHelper::new("vpn-proxy-helper.exe", runner);

    let response = helper
        .execute(&proxifyre_apply_request(
            r"C:\ProgramData\VpnProxy\generated\proxifyre-app-config.json",
            "ProxiFyreService",
        ))
        .expect("helper response");

    assert!(response.success);
    assert_eq!(response.action, HelperAction::ProxyfierApply);
    assert_eq!(response.details["serviceName"], "ProxiFyreService");
}

#[test]
fn helper_runner_receives_json_stdin_and_elevation_flag() {
    let runner = MockRunner {
        output: HelperCommandOutput {
            status_code: 0,
            stdout: r#"{"success":true,"action":"service.restart","changed":true,"message":"Restarted","details":{}}"#.to_string(),
            stderr: String::new(),
        },
        seen: RefCell::new(Vec::new()),
    };
    let helper = StructuredHelper::new("vpn-proxy-helper.exe", runner);
    let request = service_request(ComponentId::Proxyfier, HelperAction::ServiceRestart);

    let _ = helper.execute(&request).expect("helper response");
    let seen = helper.runner().seen.borrow();
    let spec = seen.first().expect("runner should be called");
    let stdin: serde_json::Value = serde_json::from_str(&spec.stdin).expect("stdin json");

    assert_eq!(spec.program, PathBuf::from("vpn-proxy-helper.exe"));
    assert_eq!(spec.args, vec!["--json"]);
    assert!(spec.requires_elevation);
    assert_eq!(stdin["action"], "service.restart");
    assert_eq!(stdin["component"], "proxyfier");
}

#[test]
fn install_requests_are_explicit_component_actions() {
    let control = install_request(ComponentId::ControlApp);
    let proxyfier = install_request(ComponentId::Proxyfier);
    let singbox = install_request(ComponentId::Singbox);

    assert_eq!(control.action, HelperAction::InstallControlApp);
    assert_eq!(proxyfier.action, HelperAction::InstallProxyfier);
    assert_eq!(singbox.action, HelperAction::InstallSingbox);
    assert!(helper_action_requires_elevation(&proxyfier.action));
}

#[test]
fn apply_request_does_not_encode_installer_action() {
    let request = proxifyre_apply_request(
        r"C:\ProgramData\VpnProxy\generated\proxifyre-app-config.json",
        "ProxiFyreService",
    );

    assert_eq!(request.action, HelperAction::ProxyfierApply);
    assert_eq!(request.component, Some(ComponentId::Proxyfier));
    assert_eq!(
        request.payload["configPath"],
        r"C:\ProgramData\VpnProxy\generated\proxifyre-app-config.json"
    );
}

#[test]
fn non_json_helper_stdout_is_rejected() {
    let error = parse_helper_response("Proxyfier restarted successfully")
        .expect_err("raw stdout should not be accepted");

    assert_eq!(error.code, "helper_response_decode");
}

#[test]
fn failed_helper_exit_is_structured_error() {
    let runner = MockRunner {
        output: HelperCommandOutput {
            status_code: 5,
            stdout: String::new(),
            stderr: "Access denied".to_string(),
        },
        seen: RefCell::new(Vec::new()),
    };
    let helper = StructuredHelper::new("vpn-proxy-helper.exe", runner);
    let error = helper
        .execute(&service_request(
            ComponentId::Proxyfier,
            HelperAction::ServiceRestart,
        ))
        .expect_err("failed exit should become helper error");

    assert_eq!(error.code, "helper_exit");
    assert!(error.message.contains("Access denied"));
}

struct MockRunner {
    output: HelperCommandOutput,
    seen: RefCell<Vec<HelperCommandSpec>>,
}

impl HelperCommandRunner for MockRunner {
    fn run(&self, spec: &HelperCommandSpec) -> Result<HelperCommandOutput, HelperError> {
        self.seen.borrow_mut().push(spec.clone());
        Ok(self.output.clone())
    }
}
