/// A parsed finger query.
///
/// Represents the structured result of parsing a finger query string
/// like `"user@host"`, `"@host"`, or `"user@host1@host2"`.
#[derive(Debug, Clone, PartialEq)]
pub struct Query {
    /// The user to query. `None` means list all users.
    pub user: Option<String>,
    /// The host(s) to query. The last host is the connection target.
    /// Multiple hosts indicate a forwarding chain (RFC 1288).
    pub hosts: Vec<String>,
    /// Whether to request verbose output (sends `/W` prefix).
    pub long: bool,
    /// TCP port to connect on. Default is 79.
    pub port: u16,
}

/// The default finger protocol port.
pub const DEFAULT_PORT: u16 = 79;

impl Query {
    /// Parse a query string into a `Query`.
    ///
    /// # Examples
    ///
    /// - `"user@host"` -> user=Some("user"), hosts=["host"]
    /// - `"@host"` -> user=None, hosts=["host"]
    /// - `"user@host1@host2"` -> user=Some("user"), hosts=["host1", "host2"]
    /// - `"user"` -> user=Some("user"), hosts=["localhost"]
    /// - `""` or `None` -> user=None, hosts=["localhost"]
    pub fn parse(input: Option<&str>, long: bool, port: u16) -> Query {
        let input = input.unwrap_or("");

        if input.is_empty() {
            return Query {
                user: None,
                hosts: vec!["localhost".to_string()],
                long,
                port,
            };
        }

        // Split on '@'. First part is the user (if non-empty), rest are hosts.
        let parts: Vec<&str> = input.splitn(2, '@').collect();

        if parts.len() == 1 {
            // No '@' found -- user only, default to localhost.
            return Query {
                user: Some(parts[0].to_string()),
                hosts: vec!["localhost".to_string()],
                long,
                port,
            };
        }

        // Has at least one '@'.
        let user = if parts[0].is_empty() {
            None
        } else {
            Some(parts[0].to_string())
        };

        let hosts: Vec<String> = parts[1].split('@').map(|s| s.to_string()).collect();

        Query {
            user,
            hosts,
            long,
            port,
        }
    }

    /// Returns the host to connect to (the last host in the chain).
    pub fn target_host(&self) -> &str {
        self.hosts.last().expect("hosts must not be empty")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_user_at_host() {
        let q = Query::parse(Some("user@host"), false, 79);
        assert_eq!(q.user, Some("user".to_string()));
        assert_eq!(q.hosts, vec!["host".to_string()]);
        assert!(!q.long);
        assert_eq!(q.port, 79);
    }

    #[test]
    fn parse_at_host_lists_users() {
        let q = Query::parse(Some("@host"), false, 79);
        assert_eq!(q.user, None);
        assert_eq!(q.hosts, vec!["host".to_string()]);
    }

    #[test]
    fn parse_user_only_defaults_to_localhost() {
        let q = Query::parse(Some("user"), false, 79);
        assert_eq!(q.user, Some("user".to_string()));
        assert_eq!(q.hosts, vec!["localhost".to_string()]);
    }

    #[test]
    fn parse_empty_string_defaults_to_localhost() {
        let q = Query::parse(Some(""), false, 79);
        assert_eq!(q.user, None);
        assert_eq!(q.hosts, vec!["localhost".to_string()]);
    }

    #[test]
    fn parse_none_defaults_to_localhost() {
        let q = Query::parse(None, false, 79);
        assert_eq!(q.user, None);
        assert_eq!(q.hosts, vec!["localhost".to_string()]);
    }

    #[test]
    fn parse_forwarding_chain() {
        let q = Query::parse(Some("user@host1@host2"), false, 79);
        assert_eq!(q.user, Some("user".to_string()));
        assert_eq!(q.hosts, vec!["host1".to_string(), "host2".to_string()]);
    }

    #[test]
    fn parse_forwarding_chain_no_user() {
        let q = Query::parse(Some("@host1@host2"), false, 79);
        assert_eq!(q.user, None);
        assert_eq!(q.hosts, vec!["host1".to_string(), "host2".to_string()]);
    }

    #[test]
    fn parse_long_flag_preserved() {
        let q = Query::parse(Some("user@host"), true, 79);
        assert!(q.long);
    }

    #[test]
    fn parse_custom_port_preserved() {
        let q = Query::parse(Some("user@host"), false, 7979);
        assert_eq!(q.port, 7979);
    }

    #[test]
    fn target_host_returns_last_host() {
        let q = Query::parse(Some("user@host1@host2"), false, 79);
        assert_eq!(q.target_host(), "host2");
    }

    #[test]
    fn target_host_single_host() {
        let q = Query::parse(Some("user@host"), false, 79);
        assert_eq!(q.target_host(), "host");
    }

    #[test]
    fn parse_three_host_chain() {
        let q = Query::parse(Some("user@a@b@c"), false, 79);
        assert_eq!(q.user, Some("user".to_string()));
        assert_eq!(
            q.hosts,
            vec!["a".to_string(), "b".to_string(), "c".to_string()]
        );
        assert_eq!(q.target_host(), "c");
    }
}
