use std::process::Command;

use crate::service::{ServiceState, ServiceStatus};

pub fn run_doctor_check(summary_mode: bool, stopped_only: bool) -> Option<String> {
    let mut stopped_services_count = 0;
    let service_list = get_services();
    for service_name in service_list {
        if let Some(status) = check_service_status(&service_name) {
            match status.state {
                ServiceState::Started { reason } => {
                    if !summary_mode && !stopped_only {
                        if let Some(reason) = reason {
                            println!("{} is started: {}", status.name, reason);
                        } else {
                            println!("{} is started.", status.name);
                        }
                    }
                }
                ServiceState::Stopped { reason } => {
                    stopped_services_count += 1;
                    if (!summary_mode || stopped_only)
                        && let Some(stop_reason) = reason
                    {
                        println!("{} : {}", status.name, stop_reason);
                    }
                }
                ServiceState::Unknown(reason) => {
                    if !summary_mode && !stopped_only {
                        println!("Couldn't get status for {}: {}", status.name, reason);
                    }
                }
            }
        }
    }
    if summary_mode {
        if stopped_services_count == 0 {
            Some(String::from("dinit-doctor: Everything looks healthy!"))
        } else {
            Some(String::from(format!(
                "dinit-doctor: {} stopped services",
                stopped_services_count
            )))
        }
    } else {
        None
    }
}

pub fn get_services() -> Vec<String> {
    let service_command = Command::new("dinitctl")
        .arg("list")
        .output()
        .expect("Failed to list user services");
    String::from_utf8(service_command.stdout)
        .expect("Failed to extract stdout from dinitctl list")
        .lines()
        .filter_map(|l| l.split_whitespace().nth(2).map(String::from))
        .collect()
}

fn check_service_status(service_name: &str) -> Option<ServiceStatus> {
    let status = Command::new("dinitctl")
        .args(["status", service_name])
        .output()
        .expect("Failed to get service status");
    String::from_utf8(status.stdout)
        .ok()
        .and_then(|status_output| parse_service_status(&status_output))
}

pub fn parse_service_status(output: &str) -> Option<ServiceStatus> {
    let mut name = None;
    let mut state = None;
    for line in output.lines() {
        let line = line.trim();
        let Some((key, value)) = line.split_once(": ") else {
            continue;
        };
        match key {
            "Service" => name = Some(value.to_string()),
            "State" => state = Some(parse_state(value)),
            _ => {}
        }
    }
    Some(ServiceStatus {
        name: name?,
        state: state?,
    })
}

fn parse_state(value: &str) -> ServiceState {
    if let Some((state, rest)) = value.split_once(" (") {
        let reason = rest.strip_suffix(')').unwrap_or(rest).to_string();
        match state {
            "STOPPED" => ServiceState::Stopped {
                reason: Some(reason),
            },
            "STARTED" => ServiceState::Started {
                reason: Some(reason),
            },
            other => ServiceState::Unknown(other.to_string()),
        }
    } else {
        match value {
            "STARTED" => ServiceState::Started { reason: None },
            "STOPPED" => ServiceState::Stopped { reason: None },
            other => ServiceState::Unknown(other.to_string()),
        }
    }
}
