use std::iter::zip;

use phono_models::presentation::{
    DropdownOption, Presentation, PresentationDiscriminants, RFC_3339_S, TextInputMode,
};
use serde::Deserialize;

use crate::errors::AppError;

/// A subset of an HTTP form that represents a [`Presentation`] in its component
/// parts.
///
/// WARNING: Though this was originally designed to be combined with
/// `#[serde(flatten)]`, Serde's buffering strategy causes `flatten` to break
/// deserialization of `Vec<_>` fields when provided with a sequence of length
/// == 1. `#[serde(flatten)]` should be avoided.
/// (Refer to: https://github.com/jplatte/serde_html_form/issues/25.)
#[derive(Clone, Debug, Deserialize)]
pub(crate) struct PresentationForm {
    pub(crate) presentation_tag: String,

    #[serde(default)]
    pub(crate) dropdown_option_colors: Vec<String>,

    #[serde(default)]
    pub(crate) dropdown_option_values: Vec<String>,

    #[serde(default)]
    pub(crate) text_input_mode: String,

    #[serde(default)]
    pub(crate) timestamp_format: String,
}

impl TryFrom<PresentationForm> for Presentation {
    type Error = AppError;

    fn try_from(form_value: PresentationForm) -> Result<Self, Self::Error> {
        let discriminant =
            PresentationDiscriminants::try_from(form_value.presentation_tag.as_str())?;
        Ok(match discriminant {
            PresentationDiscriminants::Dropdown => Presentation::Dropdown {
                allow_custom: false,
                options: zip(
                    form_value.dropdown_option_colors,
                    form_value.dropdown_option_values,
                )
                .map(|(color, value)| DropdownOption { color, value })
                .collect(),
            },
            PresentationDiscriminants::Numeric => Presentation::Numeric {},
            PresentationDiscriminants::Text => Presentation::Text {
                input_mode: TextInputMode::try_from(form_value.text_input_mode.as_str())?,
            },
            PresentationDiscriminants::Timestamp => Presentation::Timestamp {
                format: if form_value.timestamp_format.is_empty() {
                    RFC_3339_S.to_owned()
                } else {
                    form_value.timestamp_format
                },
                utc: true,
            },
            PresentationDiscriminants::Uuid => Presentation::Uuid {},
        })
    }
}
