use ledb::Document;
use serde_derive::{Deserialize, Serialize};

use crate::source::{SourceQuestion, SourceQuestionsBatch};

macro_rules! make {
    ($Target:ident; by {$($field:ident),+}; from $src:expr) => {$Target {$(
        $field: $src.$field
    ),+}};
    ($Target:ident; with defaults and by {$($field:ident),+}; from $src:expr) => {$Target {$(
        $field: $src.$field
    ),+ ,..$Target::default()}}
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, Document)]
pub struct BatchInfo {
    #[document(primary)]
    #[serde(default)]
    pub filename: String,
    #[serde(default)]
    pub description: String,
    #[serde(default)]
    pub author: String,
    #[serde(default)]
    pub comment: String,
    #[serde(default)]
    pub url: String,
    #[serde(default)]
    pub date: String,
    #[serde(default)]
    pub processed_by: String,
    #[serde(default)]
    pub redacted_by: String,
    #[serde(default)]
    pub copyright: String,
    #[serde(default)]
    pub theme: String,
    #[serde(default)]
    pub kind: String,
    #[serde(default)]
    pub source: String,
    #[serde(default)]
    pub rating: String,
}

#[derive(Debug, Default, Clone, Serialize, Deserialize, Document)]
pub struct Question {
    #[document(primary)]
    #[serde(default)]
    pub num: u32,
    #[document(index)]
    pub id: String,

    pub description: String,
    pub answer: String,

    #[serde(default)]
    pub author: String,
    #[serde(default)]
    pub comment: String,
    #[serde(default)]
    pub comment1: String,
    #[serde(default)]
    pub tour: String,
    #[serde(default)]
    pub url: String,
    #[serde(default)]
    pub date: String,
    #[serde(default)]
    pub processed_by: String,
    #[serde(default)]
    pub redacted_by: String,
    #[serde(default)]
    pub copyright: String,
    #[serde(default)]
    pub theme: String,
    #[serde(default)]
    pub kind: String,
    #[serde(default)]
    pub source: String,
    #[serde(default)]
    pub rating: String,
    #[document(nested)]
    #[serde(default)]
    pub batch_info: BatchInfo,
}

impl From<SourceQuestion> for Question {
    fn from(src: SourceQuestion) -> Self {
        make! {Self; with defaults and by {
            num, id, description, answer, author, comment, comment1, tour, url,
            date, processed_by, redacted_by, copyright, theme, kind, source, rating
        }; from src}
    }
}

impl From<SourceQuestionsBatch> for BatchInfo {
    fn from(src: SourceQuestionsBatch) -> Self {
        make! {Self; by {
            filename, description, author, comment, url, date,
            processed_by, redacted_by, copyright, theme, kind, source, rating
        }; from src}
    }
}

impl From<SourceQuestionsBatch> for Vec<Question> {
    fn from(src: SourceQuestionsBatch) -> Self {
        let mut result: Vec<Question> = src
            .questions
            .iter()
            .map(|item| item.clone().into())
            .collect();
        let batch_info = BatchInfo::from(src);
        result.iter_mut().for_each(|mut question| {
            question.batch_info = batch_info.clone();
        });

        result
    }
}

pub trait QuestionsConverter {
    fn convert<'a>(&'a mut self) -> Box<dyn Iterator<Item = Question> + 'a>;
}

impl<T> QuestionsConverter for T
where
    T: Iterator<Item = (String, Result<SourceQuestionsBatch, serde_json::Error>)>,
{
    fn convert<'a>(&'a mut self) -> Box<dyn Iterator<Item = Question> + 'a> {
        let iter = self
            .filter(|(_, data)| data.is_ok())
            .flat_map(|(filename, data)| {
                let mut batch = data.unwrap();
                batch.filename = filename;
                let questions: Vec<Question> = batch.into();
                questions
            });
        Box::new(iter)
    }
}