use anyhow::{ensure, Result};
use hickory_proto as proto;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct DnsResponse {
    #[serde(rename = "Status")]
    pub status: u32,
    #[serde(rename = "TC")]
    pub tc: bool,
    #[serde(rename = "RD")]
    pub rd: bool,
    #[serde(rename = "RA")]
    pub ra: bool,
    #[serde(rename = "AD")]
    pub ad: bool,
    #[serde(rename = "CD")]
    pub cd: bool,
    #[serde(rename = "Question")]
    pub question: Vec<DohQuestionJson>,
    #[serde(rename = "Answer")]
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub answer: Vec<DohRecordJson>,
    #[serde(rename = "Comment")]
    pub comment: Option<String>,
    pub edns_client_subnet: Option<String>,
}
impl DnsResponse {
    pub fn from_message(message: proto::op::Message) -> Result<Self> {
        ensure!(
            message.message_type() == proto::op::MessageType::Response,
            "Expected message type to be response"
        );
        ensure!(
            message.query_count() == message.queries().len() as u16,
            "Query count mismatch"
        );
        ensure!(
            message.answer_count() == message.answers().len() as u16,
            "Answer count mismatch"
        );
        let status: u32 =
            <u16 as From<proto::op::ResponseCode>>::from(message.response_code()) as u32;
        let question: Vec<_> = message
            .queries()
            .iter()
            .map(DohQuestionJson::from_query)
            .collect();
        let answer: Vec<_> = message
            .answers()
            .iter()
            .map(DohRecordJson::from_record)
            .collect::<Result<_>>()?;
        Ok(DnsResponse {
            status,
            tc: message.truncated(),
            rd: message.recursion_desired(),
            ra: message.recursion_available(),
            ad: message.authentic_data(),
            cd: message.checking_disabled(),
            question,
            answer,
            comment: None,
            edns_client_subnet: None,
        })
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DohQuestionJson {
    pub name: String,
    #[serde(rename = "type")]
    pub question_type: u16,
}
impl DohQuestionJson {
    pub fn from_query(query: &proto::op::Query) -> Self {
        Self {
            name: query.name().to_string(),
            question_type: query.query_type().into(),
        }
    }
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DohRecordJson {
    pub name: String,
    #[serde(rename = "type")]
    pub record_type: u16,
    #[serde(rename = "TTL")]
    pub ttl: u32,
    pub data: String,
}
impl DohRecordJson {
    pub fn from_record(record: &proto::rr::Record) -> Result<Self> {
        Ok(Self {
            name: record.name().to_string(),
            record_type: record.record_type().into(),
            ttl: record.ttl(),
            data: record.data().to_string(),
        })
    }
}