use crate::geometry::{Point, Vector2, Line, Circle, Arc, Ellipse, Polyline, BSpline, NURBS, Curve};
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MeasurementResult {
    pub value: f64,
    pub unit: MeasurementUnit,
    pub display: String,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum MeasurementUnit {
    Millimeters,
    Centimeters,
    Meters,
    Inches,
    Feet,
    Degrees,
    Radians,
}

impl MeasurementUnit {
    pub fn abbreviation(&self) -> &str {
        match self {
            MeasurementUnit::Millimeters => "mm",
            MeasurementUnit::Centimeters => "cm",
            MeasurementUnit::Meters => "m",
            MeasurementUnit::Inches => "\"",
            MeasurementUnit::Feet => "'",
            MeasurementUnit::Degrees => "°",
            MeasurementUnit::Radians => "rad",
        }
    }
    
    pub fn conversion_factor(&self) -> f64 {
        match self {
            MeasurementUnit::Millimeters => 1.0,
            MeasurementUnit::Centimeters => 10.0,
            MeasurementUnit::Meters => 1000.0,
            MeasurementUnit::Inches => 25.4,
            MeasurementUnit::Feet => 304.8,
            MeasurementUnit::Degrees => 1.0,
            MeasurementUnit::Radians => 1.0,
        }
    }
}

pub trait MeasurementTool {
    fn measure(&self) -> MeasurementResult;
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult;
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct DistanceMeasurement {
    pub start_point: Point,
    pub end_point: Point,
    pub distance: f64,
    pub delta_x: f64,
    pub delta_y: f64,
    pub angle: f64,
    pub unit: MeasurementUnit,
}

impl DistanceMeasurement {
    pub fn new(start: Point, end: Point, unit: MeasurementUnit) -> Self {
        let delta = end - start;
        let distance = (delta.x.powi(2) + delta.y.powi(2)).sqrt();
        let angle = delta.to_vector2().angle();
        
        Self {
            start_point: start,
            end_point: end,
            distance,
            delta_x: delta.x,
            delta_y: delta.y,
            angle,
            unit,
        }
    }
    
    pub fn distance_in_unit(&self, unit: MeasurementUnit) -> f64 {
        let base_distance = self.distance * self.unit.conversion_factor();
        base_distance / unit.conversion_factor()
    }
    
    pub fn format(&self, decimals: u32) -> String {
        format!(
            "Distance: {:.3$} | Δx: {:.3$} | Δy: {:.3$} | Angle: {:.2$}°",
            self.distance,
            self.delta_x,
            self.delta_y,
            self.angle,
            decimals
        )
    }
}

impl MeasurementTool for DistanceMeasurement {
    fn measure(&self) -> MeasurementResult {
        MeasurementResult {
            value: self.distance,
            unit: self.unit,
            display: format!("{:.3} {}", self.distance, self.unit.abbreviation()),
        }
    }
    
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult {
        MeasurementResult {
            value: self.distance,
            unit: self.unit,
            display: format!("{:.1$} {}", self.distance, decimals, self.unit.abbreviation()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AngleMeasurement {
    pub vertex: Point,
    pub start_point: Point,
    pub end_point: Point,
    pub angle: f64,
    pub angle_rad: f64,
    pub is_reflex: bool,
    pub unit: MeasurementUnit,
}

impl AngleMeasurement {
    pub fn new(vertex: Point, p1: Point, p2: Point, unit: MeasurementUnit) -> Self {
        let v1 = (p1 - vertex).to_vector2();
        let v2 = (p2 - vertex).to_vector2();
        
        let dot = v1.x * v2.x + v1.y * v2.y;
        let cross = v1.x * v2.y - v1.y * v2.x;
        let angle_rad = cross.atan2(dot);
        
        let angle_deg = angle_rad.to_degrees();
        let normalized_angle = if angle_deg < 0.0 { angle_deg + 360.0 } else { angle_deg };
        let is_reflex = normalized_angle > 180.0;
        
        Self {
            vertex,
            start_point: p1,
            end_point: p2,
            angle: normalized_angle,
            angle_rad,
            is_reflex,
            unit,
        }
    }
    
    pub fn get_smallest_angle(&self) -> f64 {
        self.angle.min(360.0 - self.angle)
    }
    
    pub fn angle_in_unit(&self, unit: MeasurementUnit) -> f64 {
        if unit == MeasurementUnit::Radians {
            self.angle_rad.abs()
        } else {
            self.angle
        }
    }
    
    pub fn format(&self, decimals: u32) -> String {
        if self.is_reflex {
            format!(
                "Angle: {:.2$}° (reflex: {:.2$}°)",
                self.angle,
                360.0 - self.angle,
                decimals
            )
        } else {
            format!("Angle: {:.2$}°", self.angle, decimals)
        }
    }
}

impl MeasurementTool for AngleMeasurement {
    fn measure(&self) -> MeasurementResult {
        MeasurementResult {
            value: self.angle,
            unit: self.unit,
            display: format!("{:.2}°", self.angle),
        }
    }
    
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult {
        MeasurementResult {
            value: self.angle,
            unit: self.unit,
            display: format!("{:.1$}°", self.angle, decimals),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AreaMeasurement {
    pub area: f64,
    pub perimeter: f64,
    pub centroid: Point,
    pub unit: MeasurementUnit,
    pub points: Vec<Point>,
}

impl AreaMeasurement {
    pub fn new(points: Vec<Point>, unit: MeasurementUnit) -> Self {
        let area = Self::calculate_area(&points);
        let perimeter = Self::calculate_perimeter(&points);
        let centroid = Self::calculate_centroid(&points);
        
        Self {
            area,
            perimeter,
            centroid,
            unit,
            points,
        }
    }
    
    fn calculate_area(points: &[Point]) -> f64 {
        if points.len() < 3 {
            return 0.0;
        }
        
        let mut area = 0.0;
        let n = points.len();
        
        for i in 0..n {
            let j = (i + 1) % n;
            area += points[i].x * points[j].y;
            area -= points[j].x * points[i].y;
        }
        
        area.abs() / 2.0
    }
    
    fn calculate_perimeter(points: &[Point]) -> f64 {
        let mut perimeter = 0.0;
        
        for i in 0..points.len() {
            let next = (i + 1) % points.len();
            perimeter += points[i].distance_to(&points[next]);
        }
        
        perimeter
    }
    
    fn calculate_centroid(points: &[Point]) -> Point {
        if points.is_empty() {
            return Point::origin();
        }
        
        let cx = points.iter().map(|p| p.x).sum::<f64>() / points.len() as f64;
        let cy = points.iter().map(|p| p.y).sum::<f64>() / points.len() as f64;
        
        Point::new(cx, cy, 0.0)
    }
    
    pub fn area_in_unit(&self, unit: MeasurementUnit) -> f64 {
        let base_area = self.area * self.unit.conversion_factor().powi(2);
        base_area / unit.conversion_factor().powi(2)
    }
    
    pub fn perimeter_in_unit(&self, unit: MeasurementUnit) -> f64 {
        let base_perimeter = self.perimeter * self.unit.conversion_factor();
        base_perimeter / unit.conversion_factor()
    }
    
    pub fn format(&self, decimals: u32) -> String {
        format!(
            "Area: {:.3$} {}² | Perimeter: {:.3$} {}",
            self.area,
            self.unit.abbreviation(),
            self.perimeter,
            self.unit.abbreviation(),
            decimals
        )
    }
}

impl MeasurementTool for AreaMeasurement {
    fn measure(&self) -> MeasurementResult {
        MeasurementResult {
            value: self.area,
            unit: self.unit,
            display: format!("{:.3} {}^2", self.area, self.unit.abbreviation()),
        }
    }
    
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult {
        MeasurementResult {
            value: self.area,
            unit: self.unit,
            display: format!("{:.1$} {}^2", self.area, decimals, self.unit.abbreviation()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RadiusMeasurement {
    pub center: Point,
    pub point: Point,
    pub radius: f64,
    pub diameter: f64,
    pub unit: MeasurementUnit,
}

impl RadiusMeasurement {
    pub fn new(center: Point, point: Point, unit: MeasurementUnit) -> Self {
        let radius = center.distance_to(&point);
        
        Self {
            center,
            point,
            radius,
            diameter: radius * 2.0,
            unit,
        }
    }
    
    pub fn from_circle(circle: &Circle, unit: MeasurementUnit) -> Self {
        Self {
            center: circle.center,
            point: Point::new(
                circle.center.x + circle.radius,
                circle.center.y,
                0.0,
            ),
            radius: circle.radius,
            diameter: circle.radius * 2.0,
            unit,
        }
    }
    
    pub fn from_arc(arc: &Arc, unit: MeasurementUnit) -> Self {
        Self {
            center: arc.center,
            point: Point::new(
                arc.center.x + arc.radius,
                arc.center.y,
                0.0,
            ),
            radius: arc.radius,
            diameter: arc.radius * 2.0,
            unit,
        }
    }
    
    pub fn radius_in_unit(&self, unit: MeasurementUnit) -> f64 {
        let base_radius = self.radius * self.unit.conversion_factor();
        base_radius / unit.conversion_factor()
    }
    
    pub fn format(&self, decimals: u32) -> String {
        format!(
            "Radius: {:.3$} | Diameter: {:.3$}",
            self.radius,
            self.diameter,
            decimals
        )
    }
}

impl MeasurementTool for RadiusMeasurement {
    fn measure(&self) -> MeasurementResult {
        MeasurementResult {
            value: self.radius,
            unit: self.unit,
            display: format!("R {:.3} {}", self.radius, self.unit.abbreviation()),
        }
    }
    
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult {
        MeasurementResult {
            value: self.radius,
            unit: self.unit,
            display: format!("R {:.1$} {}", self.radius, decimals, self.unit.abbreviation()),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ArcLengthMeasurement {
    pub arc: Arc,
    pub arc_length: f64,
    pub chord_length: f64,
    pub sweep_angle: f64,
    pub unit: MeasurementUnit,
}

impl ArcLengthMeasurement {
    pub fn new(arc: Arc, unit: MeasurementUnit) -> Self {
        let arc_length = arc.radius * (arc.end_angle - arc.start_angle).abs();
        
        let start = Point::new(
            arc.center.x + arc.radius * arc.start_angle.cos(),
            arc.center.y + arc.radius * arc.start_angle.sin(),
            0.0,
        );
        let end = Point::new(
            arc.center.x + arc.radius * arc.end_angle.cos(),
            arc.center.y + arc.radius * arc.end_angle.sin(),
            0.0,
        );
        let chord_length = start.distance_to(&end);
        
        let sweep_angle = (arc.end_angle - arc.start_angle).abs().to_degrees();
        
        Self {
            arc,
            arc_length,
            chord_length,
            sweep_angle,
            unit,
        }
    }
    
    pub fn format(&self, decimals: u32) -> String {
        format!(
            "Arc Length: {:.3$} | Chord: {:.3$} | Angle: {:.2$}°",
            self.arc_length,
            self.chord_length,
            self.sweep_angle,
            decimals
        )
    }
}

impl MeasurementTool for ArcLengthMeasurement {
    fn measure(&self) -> MeasurementResult {
        MeasurementResult {
            value: self.arc_length,
            unit: self.unit,
            display: format!("{:.3} {}", self.arc_length, self.unit.abbreviation()),
        }
    }
    
    fn measure_with_precision(&self, decimals: u32) -> MeasurementResult {
        MeasurementResult {
            value: self.arc_length,
            unit: self.unit,
            display: format!("{:.1$} {}", self.arc_length, decimals, self.unit.abbreviation()),
        }
    }
}

pub struct MeasurementCalculator;

impl MeasurementCalculator {
    pub fn calculate_distance(start: Point, end: Point, unit: MeasurementUnit) -> DistanceMeasurement {
        DistanceMeasurement::new(start, end, unit)
    }
    
    pub fn calculate_angle(vertex: Point, p1: Point, p2: Point, unit: MeasurementUnit) -> AngleMeasurement {
        AngleMeasurement::new(vertex, p1, p2, unit)
    }
    
    pub fn calculate_area_from_points(points: Vec<Point>, unit: MeasurementUnit) -> AreaMeasurement {
        AreaMeasurement::new(points, unit)
    }
    
    pub fn calculate_radius(center: Point, point: Point, unit: MeasurementUnit) -> RadiusMeasurement {
        RadiusMeasurement::new(center, point, unit)
    }
    
    pub fn calculate_circle_measurements(circle: &Circle, unit: MeasurementUnit) -> (RadiusMeasurement, AreaMeasurement) {
        let radius = RadiusMeasurement::from_circle(circle, unit);
        
        let circumference_points = vec![
            Point::new(circle.center.x + circle.radius, circle.center.y, 0.0),
            Point::new(circle.center.x, circle.center.y + circle.radius, 0.0),
            Point::new(circle.center.x - circle.radius, circle.center.y, 0.0),
            Point::new(circle.center.x, circle.center.y - circle.radius, 0.0),
        ];
        let area = AreaMeasurement::new(circumference_points, unit);
        
        (radius, area)
    }
    
    pub fn calculate_curve_length(curve: &Curve, tolerance: f64, unit: MeasurementUnit) -> f64 {
        match curve {
            Curve::Line(line) => line.length() * unit.conversion_factor(),
            Curve::Circle(circle) => 2.0 * std::f64::consts::PI * circle.radius * unit.conversion_factor(),
            Curve::Arc(arc) => arc.radius * (arc.end_angle - arc.start_angle).abs() * unit.conversion_factor(),
            _ => 0.0,
        }
    }
    
    pub fn convert_distance(value: f64, from: MeasurementUnit, to: MeasurementUnit) -> f64 {
        let base = value * from.conversion_factor();
        base / to.conversion_factor()
    }
    
    pub fn convert_area(value: f64, from: MeasurementUnit, to: MeasurementUnit) -> f64 {
        let base = value * from.conversion_factor().powi(2);
        base / to.conversion_factor().powi(2)
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MeasurementSession {
    pub measurements: Vec<MeasurementResult>,
    pub session_start: std::time::SystemTime,
    pub unit: MeasurementUnit,
}

impl MeasurementSession {
    pub fn new(unit: MeasurementUnit) -> Self {
        Self {
            measurements: Vec::new(),
            session_start: std::time::SystemTime::now(),
            unit,
        }
    }
    
    pub fn add_measurement(&mut self, measurement: MeasurementResult) {
        self.measurements.push(measurement);
    }
    
    pub fn clear(&mut self) {
        self.measurements.clear();
    }
    
    pub fn get_statistics(&self) -> MeasurementStatistics {
        if self.measurements.is_empty() {
            return MeasurementStatistics::empty();
        }
        
        let values: Vec<f64> = self.measurements.iter().map(|m| m.value).collect();
        let sum: f64 = values.iter().sum();
        let count = values.len() as f64;
        let avg = sum / count;
        
        let min = *values.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
        let max = *values.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap();
        
        MeasurementStatistics {
            count: self.measurements.len(),
            sum,
            average: avg,
            minimum: min,
            maximum: max,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct MeasurementStatistics {
    pub count: usize,
    pub sum: f64,
    pub average: f64,
    pub minimum: f64,
    pub maximum: f64,
}

impl MeasurementStatistics {
    fn empty() -> Self {
        Self {
            count: 0,
            sum: 0.0,
            average: 0.0,
            minimum: 0.0,
            maximum: 0.0,
        }
    }
    
    pub fn format(&self, unit: MeasurementUnit) -> String {
        if self.count == 0 {
            "No measurements".to_string()
        } else {
            format!(
                "Count: {} | Sum: {:.3} | Avg: {:.3} | Min: {:.3} | Max: {:.3} {}",
                self.count,
                self.sum,
                self.average,
                self.minimum,
                self.maximum,
                unit.abbreviation()
            )
        }
    }
}
