// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 lacklustr@protonmail.com https://github.com/eadf

use crate::common::macros::{integrity_assert, integrity_assert_eq, integrity_assert_unique};
use crate::corner_table::TriangleIndex;
use crate::corner_table::common::CornerTableData;
use crate::{
    common::VertexIndex,
    corner_table::{CornerIndex, CornerTable},
};

impl<const ENABLE_UNSAFE: bool> CornerTable<ENABLE_UNSAFE> {
    #[inline(always)]
    pub(crate) fn opposite_corners(&self) -> &[CornerIndex] {
        &self.data.opposite_corner_vec
    }

    #[inline(always)]
    pub(crate) fn corner_of_vertices(&self) -> &[CornerIndex] {
        &self.data.corner_of_vertex_vec
    }

    /// gives an iterator over every valid corner
    pub(crate) fn iter_valid_corners(&self) -> impl Iterator<Item = CornerIndex> {
        self.data
            .vertex_of_corner_vec
            .iter()
            .enumerate()
            .filter_map(|(c, &v)| v.is_valid().then_some(CornerIndex(c as u32)))
    }

    /// Asserts that all corners of the c0 corner fan refer to the same vertex
    pub(crate) fn assert_corner_fan_vertices(&self, c0: CornerIndex) {
        let v0 = self.vertex(c0);
        for c in self.iter_ccw_swing(c0).skip(1) {
            integrity_assert_eq!(
                self.vertex(c),
                v0,
                "Corner {:?} has vertex {:?} but expected {:?} (same as corner {:?})",
                c,
                self.vertex(c),
                v0,
                c0
            );
        }
    }

    pub(crate) fn assert_valid_corner(&self, c0: CornerIndex) {
        assert!(!self.is_triangle_deleted(c0.into()));
        let o = self.data.opposite(c0);
        assert!(o.is_valid());
        assert!(!self.is_triangle_deleted(o.into()));
        assert!(self.next(c0).is_valid());
        assert!(self.prev(c0).is_valid());
        let v = self.vertex(c0);
        assert!(
            v.is_valid(),
            "Corner has no Vertex:{}",
            self.data.dbg_corner(c0)
        );
        let vc = self.corner(v);
        assert!(vc.is_valid());
        // make sure one of the corners in the fan is the default corner of the vertex
        for (i, c) in self.iter_ccw_swing(c0).enumerate() {
            assert!(i < 100); // no infinite loops
            assert!(c.is_valid());
            assert!(!self.is_triangle_deleted(c.into()));
            // all corners in fan must use the same vertex
            assert_eq!(v, self.vertex(c));
        }
    }

    /// Asserts that a triangle's three corners reference three distinct vertices
    pub(crate) fn assert_triangle_vertices_distinct(&self, c: CornerIndex) {
        use crate::common::macros::integrity_assert_ne;

        let v0 = self.vertex(c);
        let v1 = self.vertex(self.next(c));
        let v2 = self.vertex(self.prev(c));

        integrity_assert_ne!(v0, v1, "Triangle {:?}: corners share vertex", c.triangle());
        integrity_assert_ne!(v0, v2, "Triangle {:?}: corners share vertex", c.triangle());
        integrity_assert_ne!(v1, v2, "Triangle {:?}: corners share vertex", c.triangle());
    }

    pub(crate) fn dbg_triangle_context(&self, context: impl AsRef<str>, c0: CornerIndex) {
        let context = context.as_ref();
        let (c1, c2) = c0.next_prev();
        let v0 = self.data.vertex_of_corner(c0);
        let o0 = self.data.opposite(c0);

        let v1 = self.data.vertex_of_corner(c1);
        let o1 = self.data.opposite(c1);

        let v2 = self.data.vertex_of_corner(c2);
        let o2 = self.data.opposite(c2);

        println!(
            "triangle: {context} {c0:?}:v{v0:?}o{o0:?} {c1:?}:v{v1:?}o{o1:?} {c2:?}:v{v2:?}o{o2:?}"
        );
    }

    pub(crate) fn dbg_triangle(&self, c: CornerIndex) -> String {
        // use the non-checked variant for next and prev in dbg mode
        let (cn, cp) = c.next_prev();
        format!(
            "{},{},{}",
            self.data.dbg_corner(c),
            self.data.dbg_corner(cn),
            self.data.dbg_corner(cp)
        )
    }

    pub(crate) fn dbg_vertex_fan(&self, vertex: VertexIndex) -> String {
        let corner = self.corner(vertex);
        self.iter_ccw_swing(corner)
            .map(|c| self.dbg_corner(c))
            .collect::<Vec<_>>()
            .join(",")
    }

    /// Asserts the opposite corner (across the edge) of both ca and cb (pointing to each other)
    pub(crate) fn assert_dual_opposite(&self, ca: CornerIndex, cb: CornerIndex) {
        integrity_assert!(ca.is_valid());
        integrity_assert!(cb.is_valid());
        integrity_assert_eq!(
            self.data.opposite_corner_vec[ca.0 as usize],
            cb,
            "ca:{}.opposite != cb:{}",
            self.data.dbg_corner(ca),
            self.dbg_triangle(cb)
        );
        integrity_assert_eq!(
            self.data.opposite_corner_vec[cb.0 as usize],
            ca,
            "cb:{}.opposite != ca:{}",
            self.data.dbg_corner(cb),
            self.dbg_triangle(ca)
        );
    }

    pub(crate) fn get_vertex_valence(&self, vertex: VertexIndex) -> i16 {
        integrity_assert!(vertex.is_valid());
        let start_corner = self.corner(vertex);
        integrity_assert!(start_corner.is_valid());
        self.valence(start_corner)
    }

    // Should be run on a triangle we know is deleted.
    pub(crate) fn check_deleted_triangle(&self, triangle: TriangleIndex) {
        assert!(self.is_triangle_deleted(triangle));

        let c = triangle.base_corner();
        let (cn, cp) = c.next_prev();

        // if V(c) == invalid, V(cn) and V(cp) should be too
        assert!(
            !self.data.vertex_of_corner(c).is_valid(),
            "Triangle {} is deleted, but still has a valid vertex",
            self.data.dbg_corner(c)
        );
        assert!(
            !self.data.vertex_of_corner(cn).is_valid(),
            "Triangle {} is deleted, but still has a valid vertex",
            self.data.dbg_corner(cn)
        );
        assert!(
            !self.data.vertex_of_corner(cp).is_valid(),
            "Triangle {} is deleted, but still has a valid vertex",
            self.data.dbg_corner(cp)
        );

        assert!(
            !self.data.opposite(c).is_valid(),
            "Triangle {} is deleted, but still has a valid opposite",
            self.data.dbg_corner(c)
        );
        assert!(
            !self.data.opposite(cn).is_valid(),
            "Triangle {} is deleted, but still has a valid opposite",
            self.data.dbg_corner(cn)
        );
        assert!(
            !self.data.opposite(cp).is_valid(),
            "Triangle {} is deleted, but still has a valid opposite",
            self.data.dbg_corner(cp)
        );
    }

    /// Comprehensive integrity check for the corner table
    /// Returns Ok(()) if valid, Err(String) with detailed error description if invalid
    pub fn check_integrity(&self, num_vertices: u32) -> Result<(), String> {
        let num_corners = self.len() as u32;
        let num_triangles = num_corners / 3;

        for c in self.iter_valid_corners() {
            let o = self.opposite(c);
            {
                if !o.is_valid() {
                    return Err(
                        format!("{c:?} pointed to an invalid opposite corner: {o:?}").to_string(),
                    );
                }
                if self.is_triangle_deleted(o.triangle()) {
                    return Err(format!(
                        "{c:?} pointed to a deleted, but valid, opposite corner: {}",
                        self.dbg_triangle(o)
                    )
                    .to_string());
                }
                let triangle = o.into();
                if self.is_triangle_deleted(triangle) {
                    return Err(format!(
                        "{c:?} pointed to a deleted opposite triangle {triangle:?} {o:?}"
                    )
                    .to_string());
                }
            }
            {
                let c0 = o;
                let (c1, c2) = self.next_prev(c0);
                let v0 = self.vertex(c0);
                let v1 = self.vertex(c1);
                let v2 = self.vertex(c2);
                integrity_assert!(v0.is_valid());
                integrity_assert!(v1.is_valid());
                integrity_assert!(v2.is_valid());
                integrity_assert_unique!(v0, v1, v2);
            }
            {
                // check V(c) != V(O(c))
                let v_c = self.vertex(c);
                let v_o = self.vertex(o);
                if v_c == v_o {
                    return Err(format!(
                        "V({c:?})=V{v_c:?} == V(O({c:?})={o:?})=V{v_o:?} (check V(c)!=V(O(c)))"
                    )
                    .to_string());
                }
            }
            {
                // check O(O(c)) = c
                let o_c = self.opposite(c);
                let o_o_c = self.opposite(o_c);
                if o_o_c != c {
                    return Err(format!("O(O({c:?}))={o_o_c:?} != c={c:?}").to_string());
                }
            }
            {
                // check V(c.next()) == V(O(c).prev())
                let o_p = self.prev(o);
                let c_n = self.next(c);
                let v_c_n = self.vertex(c_n);
                let v_o_p = self.vertex(o_p);

                if v_c_n != v_o_p {
                    return Err(format!(
                        "V({c:?}.next()={c_n:?})={v_c_n:?} != V(O({o:?}).prev())={o_p:?})={v_o_p:?}"
                    )
                    .to_string());
                }
            }

            {
                // check V(c.prev()) == V(O(c).next())
                let o_n = self.next(o);
                let c_p = self.prev(c);
                let v_o_n = self.vertex(c_p);
                let v_c_p = self.vertex(o_n);

                if v_o_n != v_c_p {
                    return Err(format!(
                        "V({c:?}.prev():{c_p:?}){v_o_n:?} != V({o:?}.next():{o_n:?})={v_c_p:?} "
                    )
                    .to_string());
                }
            }

            {
                // check c.next().next().next() = c
                let c_n = self.next(c);
                let c_n_n = self.next(c_n);
                let c_n_n_n = self.next(c_n_n);
                if c_n_n_n != c {
                    return Err(
                        format!("c.next().next().next()={c_n_n_n:?} != c={c:?}").to_string()
                    );
                }
            }

            {
                // check c.prev().prev().prev() = c
                let c_p = self.next(c);
                let c_p_p = self.next(c_p);
                let c_p_p_p = self.next(c_p_p);
                if c_p_p_p != c {
                    return Err(
                        format!("c.prev().prev().prev()={c_p_p_p:?} != c={c:?}").to_string()
                    );
                }
            }

            {
                // check c.twin.twin == c
                if self.is_corner_deleted(c) {
                    let twin = self.data.twin_unchecked(c);
                    if twin.is_valid() {
                        assert_eq!(twin, self.twin(c));
                        return Err(format!(
                            "{c:?}.twin()={twin:?} was valid when corner {c:?} was deleted"
                        )
                        .to_string());
                    }
                } else {
                    let twin = self.data.twin_unchecked(c);
                    if self.is_corner_deleted(twin) {
                        return Err(format!(
                            "{c:?}.twin()={twin:?} was deleted when corner={c:?} was not"
                        )
                        .to_string());
                    } else {
                        assert_eq!(
                            twin,
                            self.twin(c),
                            "twin_unchecked():{twin:?} != twin():{:?}",
                            self.twin(c)
                        );
                    }
                    let ctt = self.data.twin_unchecked(twin);

                    if ctt != c {
                        return Err(format!("c.twin().twin()={ctt:?} != c={c:?}").to_string());
                    }
                }
            }

            {
                // check c.swing_ccw.swing_cw == c
                let css = self.swing_cw(self.swing_ccw(c));
                if css != c {
                    return Err(format!("c.swing_ccw().swing_cw()={css:?} != c={c:?}").to_string());
                }
            }
        }

        // Basic size checks
        if num_corners == 0 {
            return Err("Empty corner table".to_string());
        }

        if !num_corners.is_multiple_of(3) {
            return Err(format!("Corner count {num_corners} is not divisible by 3",));
        }

        if self.opposite_corners().len() as u32 != num_corners {
            return Err(format!(
                "Opposite corner array size {} doesn't match vertex_of_corner size {num_corners}",
                self.opposite_corners().len(),
            ));
        }

        // Check vertex indices are valid
        for (corner, &vertex) in self.vertex_of_corners().iter().enumerate() {
            if vertex.0 >= num_vertices && vertex.is_valid() {
                return Err(format!(
                    "Corner {corner} references invalid vertex {} (max: {})",
                    vertex.0,
                    num_vertices - 1
                ));
            }
        }

        // Check corner_of_vertex array
        if self.corner_of_vertices().len() as u32 != num_vertices {
            return Err(format!(
                "corner_of_vertex size {} doesn't match vertex count {num_vertices}",
                self.corner_of_vertices().len(),
            ));
        }

        // Check that corner_of_vertex references are valid
        for (vertex, &corner) in self.corner_of_vertices().iter().enumerate() {
            let vertex = VertexIndex(vertex as u32);
            if corner.is_valid() {
                if corner.0 >= num_corners {
                    return Err(format!(
                        "Vertex {vertex:?} references invalid corner {corner:?} (max: {})",
                        num_corners - 1
                    ));
                }

                if self.vertex(corner) != vertex {
                    return Err(format!(
                        "Vertex {vertex:?} references corner {} but that corner points to vertex {:?}",
                        self.dbg_corner(corner),
                        self.vertex(corner)
                    ));
                }
            }
        }

        // Check opposite corner relationships
        for (corner, &opposite) in self.opposite_corners().iter().enumerate() {
            if self.is_triangle_deleted(CornerIndex(corner as u32).triangle()) {
                continue;
            }

            if !self.vertex_of_corners()[corner].is_valid() {
                continue;
            }
            let corner = CornerIndex(corner as u32);
            if !opposite.is_valid() {
                return Err(format!(
                    "Corner {corner:?} has invalid opposite corner (max: {})",
                    num_corners - 1
                ));
            }
            if opposite.is_valid() {
                if opposite.0 >= num_corners {
                    return Err(format!(
                        "Corner {corner:?} has invalid opposite corner {opposite:?} (max: {})",
                        num_corners - 1
                    ));
                }

                // Opposite relationship should be symmetric
                if self.opposite_corners()[opposite.0 as usize] != corner {
                    return Err(format!(
                        "Corner {} points to opposite {}, but {} points to {} (not symmetric)",
                        self.data.dbg_corner(corner),
                        self.data.dbg_corner(opposite),
                        self.data.dbg_corner(opposite),
                        self.data
                            .dbg_corner(self.opposite_corners()[opposite.0 as usize]),
                    ));
                }

                // Check that opposite corners form a valid edge
                let next_corner = self.next(corner);
                let edge_v1 = self.vertex(next_corner);
                let prev_corner = self.prev(corner);
                let edge_v2 = self.vertex(prev_corner);

                let opp_next = self.next(opposite);
                let opp_v1 = self.vertex(opp_next);
                let opp_prev = self.prev(opposite);
                let opp_v2 = self.vertex(opp_prev);

                // The edge should be reversed on the opposite side
                if !(edge_v1 == opp_v2 && edge_v2 == opp_v1) {
                    return Err(format!(
                        "Corner {corner:?} (edge {edge_v1:?}->{edge_v2:?}) and opposite {opposite:?} (edge {opp_v1:?}->{opp_v2:?}) don't form a valid shared edge",
                    ));
                }

                // Additional checks
                let corner_next = self.next(corner);
                let corner_prev = self.prev(corner);

                let opposite_next = self.next(opposite);
                let opposite_prev = self.prev(opposite);

                // corner_a.next().vertex() should equal corner_b.prev().vertex()
                if self.vertex(corner_next) != self.vertex(opposite_prev) {
                    return Err(format!(
                        "Corner {corner:?}.next().vertex() ({:?}) != opposite {opposite:?}.prev().vertex() ({:?})",
                        self.vertex(corner_next),
                        self.vertex(opposite_prev)
                    ));
                }

                // corner_b.next().vertex() should equal corner_a.prev().vertex()
                if self.vertex(opposite_next) != self.vertex(corner_prev) {
                    return Err(format!(
                        "Corner {opposite:?}.next().vertex() ({:?}) != corner {corner:?}.prev().vertex() ({:?})",
                        self.vertex(opposite_next),
                        self.vertex(corner_prev)
                    ));
                }
            }
        }

        // Check for degenerate triangles
        for triangle in 0..num_triangles {
            let c0 = CornerIndex(triangle * 3);
            let c1 = CornerIndex(triangle * 3 + 1);
            let c2 = CornerIndex(triangle * 3 + 2);

            if !self.vertex_of_corners()[c0.0 as usize].is_valid() {
                continue;
            }

            let v0 = self.vertex(c0);
            let v1 = self.vertex(c1);
            let v2 = self.vertex(c2);

            if v0 == v1 || v1 == v2 || v2 == v0 {
                return Err(format!(
                    "Triangle {triangle} is degenerate: vertices ({v0:?}, {v1:?}, {v2:?})",
                ));
            }
        }

        // Check that every vertex is referenced by at least one corner (if it has a corner_of_vertex entry)
        let mut vertex_referenced = vec![false; num_vertices as usize];
        for &vertex in self.vertex_of_corners().iter() {
            if vertex.is_valid() {
                vertex_referenced[vertex.0 as usize] = true;
            }
        }

        for (vertex, &corner) in self.corner_of_vertices().iter().enumerate() {
            if corner.is_valid() && !vertex_referenced[vertex] {
                return Err(format!(
                    "Vertex {vertex} has corner_of_vertex entry but is not referenced by any corner",
                ));
            }
        }

        // Forward direction check:
        // Every referenced vertex must also have a valid corner_of_vertex entry
        for (vertex, &referenced) in vertex_referenced.iter().enumerate() {
            if referenced && !self.corner_of_vertices()[vertex].is_valid() {
                return Err(format!(
                    "Vertex {vertex} is referenced by some corner(s) but has INVALID_INDEX in corner_of_vertex",
                ));
            }
        }
        Ok(())
    }

    /// Quick integrity check - just the most critical invariants
    pub fn check_basic_integrity(&self, num_vertices: u32) -> Result<(), String> {
        let num_corners = self.vertex_of_corners().len() as u32;

        if !num_corners.is_multiple_of(3) {
            return Err("Corner count not divisible by 3".to_string());
        }

        if self.opposite_corners().len() as u32 != num_corners {
            return Err("Array size mismatch".to_string());
        }

        // Check for degenerate triangles
        let num_triangles = num_corners / 3;
        for triangle in 0..num_triangles {
            let c0 = CornerIndex(triangle * 3);
            let c1 = CornerIndex(triangle * 3 + 1);
            let c2 = CornerIndex(triangle * 3 + 2);

            if c2.0 >= num_corners {
                continue;
            }

            let v0 = self.vertex(c0);
            let v1 = self.vertex(c1);
            let v2 = self.vertex(c2);

            if v0.0 >= num_vertices || v1.0 >= num_vertices || v2.0 >= num_vertices {
                return Err(format!("Triangle {triangle} has invalid vertex indices"));
            }

            if v0 == v1 || v1 == v2 || v2 == v0 {
                return Err(format!(
                    "Triangle {triangle} is degenerate: ({v0:?}, {v1:?}, {v2:?})",
                ));
            }
        }

        Ok(())
    }

    #[inline(always)]
    pub(crate) fn len(&self) -> usize {
        integrity_assert_eq!(
            self.data.vertex_of_corner_vec.len(),
            self.data.opposite_corner_vec.len()
        );
        integrity_assert_eq!(
            self.data.vertex_of_corner_vec.len() as u32 / 3,
            self.triangle_pool.total_count()
        );
        self.data.vertex_of_corner_vec.len()
    }

    pub fn validate_partial(&self) -> Result<(), String> {
        // 2. Check opposite symmetry and edge matching
        for corner in (0..self.vertex_of_corners().len() as u32).map(CornerIndex) {
            if let Some(opp) = self.try_opposite(corner) {
                // Check: opposite(opposite(c)) == c
                if let Some(opp_opp) = self.try_opposite(opp) {
                    if opp_opp != corner {
                        return Err(format!(
                            "Opposite symmetry broken: opposite(opposite(C{})) = C{}, expected C{}",
                            self.dbg_corner(corner),
                            self.dbg_corner(opp_opp),
                            corner.0
                        ));
                    }
                } else {
                    return Err(format!(
                        "Opposite asymmetry: C{} points to C{}, but C{} has no opposite",
                        self.dbg_corner(corner),
                        self.dbg_corner(opp),
                        opp.0
                    ));
                }

                // Check: edge vertices match
                // c points to edge (V(next(c)), V(prev(c)))
                // opp should point to edge (V(prev(c)), V(next(c))) - reversed!
                let c_next_v = self.vertex(self.next(corner));
                let c_prev_v = self.vertex(self.prev(corner));
                let opp_next_v = self.vertex(self.next(opp));
                let opp_prev_v = self.vertex(self.prev(opp));

                if c_next_v != opp_prev_v || c_prev_v != opp_next_v {
                    return Err(format!(
                        "Edge mismatch: {} edge ({:?}→{:?}) vs {} edge ({:?}→{:?})",
                        self.dbg_corner(corner),
                        c_next_v,
                        c_prev_v,
                        self.dbg_corner(opp),
                        opp_next_v,
                        opp_prev_v
                    ));
                }
            }
        }

        // 3. Check swing operation preserves vertex
        for c in (0..self.vertex_of_corners().len() as u32).map(CornerIndex) {
            let v_at_c = self.vertex(c);

            if let Some(swung) = self.try_swing_ccw(c) {
                let v_at_swung = self.vertex(swung);
                if v_at_c != v_at_swung {
                    return Err(format!(
                        "Swing broken: C{} at V{} swung to C{} at V{}",
                        c.0, v_at_c.0, swung.0, v_at_swung.0
                    ));
                }
            }
        }

        Ok(())
    }
}

impl<const ENABLE_UNSAFE: bool> CornerTableData<ENABLE_UNSAFE> {
    pub(crate) fn twin_unchecked(&self, corner: CornerIndex) -> CornerIndex {
        self.opposite(corner.prev()).next()
    }
}
