use serde::{Deserialize, Serialize};

use crate::{
    BodyOrdinal, EdgeLabel, FaceLabel, FeatureId, Plane3, Point3, Result, SketchEntityId,
    TypesError, UnitVec3, VertexLabel,
};

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FaceFingerprint {
    pub plane: Plane3,
    pub centroid: Point3,
}

impl core::fmt::Display for FaceFingerprint {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "face@{}", self.centroid)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EdgeFingerprint {
    pub sample: Point3,
    pub direction: UnitVec3,
}

impl core::fmt::Display for EdgeFingerprint {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "edge@{}", self.sample)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VertexFingerprint {
    pub point: Point3,
}

impl core::fmt::Display for VertexFingerprint {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "vertex@{}", self.point)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum EntityFingerprint {
    Face(FaceFingerprint),
    Edge(EdgeFingerprint),
    Vertex(VertexFingerprint),
}

impl core::fmt::Display for EntityFingerprint {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Face(g) => g.fmt(f),
            Self::Edge(g) => g.fmt(f),
            Self::Vertex(g) => g.fmt(f),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum EntityRef {
    Face(FaceRef),
    Edge(EdgeRef),
    Vertex(VertexRef),
}

impl EntityRef {
    #[must_use]
    pub fn fingerprint(self) -> EntityFingerprint {
        match self {
            Self::Face(reference) => EntityFingerprint::Face(reference.fingerprint),
            Self::Edge(reference) => EntityFingerprint::Edge(reference.fingerprint),
            Self::Vertex(reference) => EntityFingerprint::Vertex(reference.fingerprint),
        }
    }

    #[must_use]
    pub const fn ordinal(self) -> BodyOrdinal {
        match self {
            Self::Face(reference) => reference.ordinal,
            Self::Edge(reference) => reference.ordinal,
            Self::Vertex(reference) => reference.ordinal,
        }
    }
}

impl core::fmt::Display for EntityRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Face(reference) => reference.label.fmt(f),
            Self::Edge(reference) => reference.label.fmt(f),
            Self::Vertex(reference) => reference.label.fmt(f),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BodyRef {
    pub feature: FeatureId,
    pub ordinal: BodyOrdinal,
}

impl BodyRef {
    #[must_use]
    pub const fn new(feature: FeatureId, ordinal: BodyOrdinal) -> Self {
        Self { feature, ordinal }
    }

    #[must_use]
    pub const fn feature(self) -> FeatureId {
        self.feature
    }

    #[must_use]
    pub const fn ordinal(self) -> BodyOrdinal {
        self.ordinal
    }
}

impl core::fmt::Display for BodyRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "body {:?}#{}", self.feature, self.ordinal)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Sketch3EntityRef {
    pub sketch: FeatureId,
    pub entity: SketchEntityId,
}

impl Sketch3EntityRef {
    #[must_use]
    pub const fn new(sketch: FeatureId, entity: SketchEntityId) -> Self {
        Self { sketch, entity }
    }
}

impl core::fmt::Display for Sketch3EntityRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "sketch {:?} entity {:?}", self.sketch, self.entity)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum DatumTarget {
    Feature(FeatureId),
    Entity(EntityRef),
    Sketch3Entity(Sketch3EntityRef),
}

impl DatumTarget {
    #[must_use]
    pub fn source_feature(self) -> FeatureId {
        match self {
            Self::Feature(feature) => feature,
            Self::Entity(entity) => match entity {
                EntityRef::Face(reference) => reference.label.feature,
                EntityRef::Edge(reference) => reference.label.feature,
                EntityRef::Vertex(reference) => reference.label.feature,
            },
            Self::Sketch3Entity(reference) => reference.sketch,
        }
    }
}

impl core::fmt::Display for DatumTarget {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            Self::Feature(feature) => write!(f, "feature {feature:?}"),
            Self::Entity(entity) => entity.fmt(f),
            Self::Sketch3Entity(reference) => reference.fmt(f),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FaceRef {
    pub label: FaceLabel,
    pub fingerprint: FaceFingerprint,
    #[serde(default, skip_serializing_if = "BodyOrdinal::is_origin")]
    pub ordinal: BodyOrdinal,
}

impl FaceRef {
    #[must_use]
    pub const fn new(label: FaceLabel, fingerprint: FaceFingerprint) -> Self {
        Self {
            label,
            fingerprint,
            ordinal: BodyOrdinal::origin(),
        }
    }

    #[must_use]
    pub const fn at(self, ordinal: BodyOrdinal) -> Self {
        Self { ordinal, ..self }
    }

    #[must_use]
    pub const fn entity_ref(self) -> EntityRef {
        EntityRef::Face(self)
    }
}

impl core::fmt::Display for FaceRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.label.fmt(f)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct EdgeRef {
    pub label: EdgeLabel,
    pub fingerprint: EdgeFingerprint,
    #[serde(default, skip_serializing_if = "BodyOrdinal::is_origin")]
    pub ordinal: BodyOrdinal,
}

impl EdgeRef {
    #[must_use]
    pub const fn new(label: EdgeLabel, fingerprint: EdgeFingerprint) -> Self {
        Self {
            label,
            fingerprint,
            ordinal: BodyOrdinal::origin(),
        }
    }

    #[must_use]
    pub const fn at(self, ordinal: BodyOrdinal) -> Self {
        Self { ordinal, ..self }
    }

    #[must_use]
    pub const fn entity_ref(self) -> EntityRef {
        EntityRef::Edge(self)
    }
}

impl core::fmt::Display for EdgeRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.label.fmt(f)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct VertexRef {
    pub label: VertexLabel,
    pub fingerprint: VertexFingerprint,
    #[serde(default, skip_serializing_if = "BodyOrdinal::is_origin")]
    pub ordinal: BodyOrdinal,
}

impl VertexRef {
    #[must_use]
    pub const fn new(label: VertexLabel, fingerprint: VertexFingerprint) -> Self {
        Self {
            label,
            fingerprint,
            ordinal: BodyOrdinal::origin(),
        }
    }

    #[must_use]
    pub const fn at(self, ordinal: BodyOrdinal) -> Self {
        Self { ordinal, ..self }
    }

    #[must_use]
    pub const fn entity_ref(self) -> EntityRef {
        EntityRef::Vertex(self)
    }
}

impl core::fmt::Display for VertexRef {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        self.label.fmt(f)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)]
#[serde(try_from = "f64", into = "f64")]
pub struct MatchScore(f64);

impl MatchScore {
    pub fn new(value: f64) -> Result<Self> {
        if value.is_finite() && (0.0..=1.0).contains(&value) {
            Ok(Self(value))
        } else {
            Err(TypesError::MatchScoreOutOfRange(value))
        }
    }

    #[must_use]
    pub const fn value(self) -> f64 {
        self.0
    }
}

impl TryFrom<f64> for MatchScore {
    type Error = TypesError;
    fn try_from(value: f64) -> Result<Self> {
        Self::new(value)
    }
}

impl From<MatchScore> for f64 {
    fn from(score: MatchScore) -> Self {
        score.0
    }
}

impl core::fmt::Display for MatchScore {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "score={}", self.0)
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum Resolution<Id> {
    Resolved(Id),
    Repaired { id: Id, score: MatchScore },
    Dangling { last_known: EntityRef },
}

impl<Id> Resolution<Id> {
    #[must_use]
    pub fn id(self) -> Option<Id> {
        match self {
            Self::Resolved(id) | Self::Repaired { id, .. } => Some(id),
            Self::Dangling { .. } => None,
        }
    }

    #[must_use]
    pub fn map<U>(self, f: impl FnOnce(Id) -> U) -> Resolution<U> {
        match self {
            Self::Resolved(id) => Resolution::Resolved(f(id)),
            Self::Repaired { id, score } => Resolution::Repaired { id: f(id), score },
            Self::Dangling { last_known } => Resolution::Dangling { last_known },
        }
    }

    #[must_use]
    pub const fn is_dangling(&self) -> bool {
        matches!(self, Self::Dangling { .. })
    }
}
