use super::exec::execute_btrfs_command;
use super::super::error::BtrfsError;
use crate::process::CommandResult;
use serde::Deserialize;
use serde::de::{self, Visitor};
use std::fmt;

pub fn execute(path: &str, json: bool, raw: bool) -> Result<CommandResult, BtrfsError> {
    let mut args = Vec::new();
    if json {
        args.push("--format");
        args.push("json");
    }
    args.push("qgroup");
    args.push("show");
    if raw {
        args.push("--raw");
    }
    args.push(path);
    execute_btrfs_command(&args)
}

#[derive(Debug, Deserialize)]
struct QgroupShowResponse {
    #[serde(rename = "qgroup-show")]
    qgroup_show: Vec<QgroupShowEntry>,
}

/// Custom deserializer for max_referenced and max_exclusive fields
/// that can handle both string values (like "none", "-") and integer values
fn deserialize_max_value<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: de::Deserializer<'de>,
{
    struct MaxValueVisitor;

    impl<'de> Visitor<'de> for MaxValueVisitor {
        type Value = String;

        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
            formatter.write_str("a string or an integer")
        }

        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            Ok(value.to_string())
        }

        fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            Ok(value.to_string())
        }

        fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
        where
            E: de::Error,
        {
            Ok(value.to_string())
        }
    }

    deserializer.deserialize_any(MaxValueVisitor)
}

#[derive(Debug, Deserialize, Clone)]
pub struct QgroupShowEntry {
    pub qgroupid: String,
    pub referenced: u64,
    #[serde(rename = "max_referenced", deserialize_with = "deserialize_max_value")]
    pub max_referenced: String,
    pub exclusive: u64,
    #[serde(rename = "max_exclusive", deserialize_with = "deserialize_max_value")]
    pub max_exclusive: String,
    pub path: String,
    pub parents: Vec<String>,
    pub children: Vec<String>,
}

impl QgroupShowEntry {
    pub fn max_referenced_parsed(&self) -> Option<u64> {
        parse_max_value(&self.max_referenced)
    }

    pub fn max_exclusive_parsed(&self) -> Option<u64> {
        parse_max_value(&self.max_exclusive)
    }
}

fn parse_max_value(value: &str) -> Option<u64> {
    let trimmed = value.trim();
    if trimmed.is_empty() || trimmed == "none" || trimmed == "-" {
        None
    } else {
        trimmed.parse().ok()
    }
}

pub fn parse(output: &str) -> Result<Vec<QgroupShowEntry>, BtrfsError> {
    let response: QgroupShowResponse = serde_json::from_str(output).map_err(|e| {
        BtrfsError::ParseError(format!("Failed to parse JSON output: {}", e))
    })?;

    Ok(response.qgroup_show)
}

pub fn execute_and_parse(path: &str, raw: bool) -> Result<Vec<QgroupShowEntry>, BtrfsError> {
    let result = execute(path, true, raw)?;
    parse(&result.stdout)
}

