use std::collections::HashMap;
use std::fmt::Display;
use std::sync::Arc;

use crate::parser::{Literal, Parameter, TypeId};
use crate::scanner::token::Number;
use crate::{Expr, PortDirection};

use super::environment::Binding;

/// Schema for a declared node type: name + ordered list of ports.
#[derive(Clone, Debug)]
pub struct NodeTypeSchema {
    pub name: String,
    pub ports: Vec<(String, PortDirection)>,
}

/// Stable unique identifier for a node instance.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NodeId(pub u64);

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

/// Stable unique identifier for a group instance.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct GroupId(pub u64);

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

/// A recorded connection between two node ports.
///
/// Internal representation held by the interpreter during evaluation. Step 6 will
/// migrate this into the implicit root group's connection list; Step 10 bridges it
/// to the public `Network`/`Connection` output type described in `README_INTEGRATION.md`.
///
#[derive(Clone, Debug)]
pub struct InternalConnection {
    pub from_node_id: NodeId,
    pub from_port: String,
    pub to_node_id: NodeId,
    pub to_port: String,
}

/// General interpreted value.
#[derive(Clone, Debug)]
pub enum Value {
    Array(Vec<Value>),
    Boolean(bool),
    Closure {
        params: Vec<Parameter>,
        return_type: Option<TypeId>,
        body: Box<Expr>,
        captured: HashMap<String, Binding>,
    },
    Group {
        id: GroupId,
        name: String,
        members: HashMap<String, Value>,
        connections: Vec<InternalConnection>,
        schema: Arc<NodeTypeSchema>,
    },
    Node {
        id: NodeId,
        name: String,
        schema: Arc<NodeTypeSchema>,
    },
    Number(Number),
    NodeType(Arc<NodeTypeSchema>),
    PortRef {
        node_id: NodeId,
        node_name: String,
        schema: Arc<NodeTypeSchema>,
        port_name: String,
    },
    Range {
        start: Number,
        end: Number,
        inclusive: bool,
    },
    Function {
        name: String,
        params: Vec<Parameter>,
        return_type: Option<TypeId>,
        body: Box<Expr>,
    },
    Ref {
        name: String,
        scope_id: u64,
    },
    String(String),
    Unit,
    Uninitialized,
}

/// Value operation errors.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum ValueError {
    InvalidAddition(&'static str, &'static str),
    InvalidSubtraction(&'static str, &'static str),
    InvalidMultiplication(&'static str, &'static str),
    InvalidDivision(&'static str, &'static str),
    InvalidModulo(&'static str, &'static str),
    DivisionByZero,
    InvalidComparison(&'static str, &'static str),
    InvalidLogicalNot(&'static str),
    InvalidLogicalAnd(&'static str, &'static str),
    InvalidLogicalOr(&'static str, &'static str),
}

impl Value {
    /// Get the type name of this value.
    pub fn type_name(&self) -> &'static str {
        match self {
            Value::Array(_) => "Array",
            Value::Boolean(_) => "bool",
            Value::Closure { .. } | Value::Function { .. } => "fn",
            Value::Group { .. } => "Group",
            Value::Node { .. } => "Node",
            Value::Number(_) => "Number",
            Value::NodeType(_) => "NodeType",
            Value::PortRef { .. } => "PortRef",
            Value::Range { .. } => "Range",
            Value::Ref { .. } => "&ref",
            Value::String(_) => "String",
            Value::Unit => "()",
            Value::Uninitialized => "<uninitialized>",
        }
    }

    pub fn add(self, other: Self) -> Result<Self, ValueError> {
        let lhs_type = self.type_name();
        let rhs_type = other.type_name();
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a + b)),
            (Value::String(a), Value::String(b)) => Ok(Value::String(a + &b)),
            (Value::String(a), Value::Number(b)) => Ok(Value::String(a + &b.to_string())),
            (Value::Number(a), Value::String(b)) => Ok(Value::String(a.to_string() + &b)),
            (Value::String(a), Value::Boolean(b)) => Ok(Value::String(a + &b.to_string())),
            (Value::Boolean(a), Value::String(b)) => Ok(Value::String(a.to_string() + &b)),
            _ => Err(ValueError::InvalidAddition(lhs_type, rhs_type)),
        }
    }

    pub fn sub(self, other: Self) -> Result<Self, ValueError> {
        let lhs_type = self.type_name();
        let rhs_type = other.type_name();
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a - b)),
            _ => Err(ValueError::InvalidSubtraction(lhs_type, rhs_type)),
        }
    }

    pub fn mul(self, other: Self) -> Result<Self, ValueError> {
        let lhs_type = self.type_name();
        let rhs_type = other.type_name();
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Number(a * b)),
            _ => Err(ValueError::InvalidMultiplication(lhs_type, rhs_type)),
        }
    }

    pub fn div(self, other: Self) -> Result<Self, ValueError> {
        let lhs_type = self.type_name();
        let rhs_type = other.type_name();
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => {
                if b == 0.0 {
                    Err(ValueError::DivisionByZero)
                } else {
                    Ok(Value::Number(a / b))
                }
            }
            _ => Err(ValueError::InvalidDivision(lhs_type, rhs_type)),
        }
    }

    pub fn rem(self, other: Self) -> Result<Self, ValueError> {
        let lhs_type = self.type_name();
        let rhs_type = other.type_name();
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => {
                if b == 0.0 {
                    Err(ValueError::DivisionByZero)
                } else {
                    Ok(Value::Number(a % b))
                }
            }
            _ => Err(ValueError::InvalidModulo(lhs_type, rhs_type)),
        }
    }

    pub fn not(&self) -> Result<Self, ValueError> {
        match self {
            Value::Boolean(b) => Ok(Value::Boolean(!*b)),
            _ => Err(ValueError::InvalidLogicalNot(self.type_name())),
        }
    }

    pub fn eq(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Array(a), Value::Array(b)) => {
                if a.len() != b.len() {
                    return Ok(Value::Boolean(false));
                }

                for (x, y) in a.iter().zip(b.iter()) {
                    if let Value::Boolean(false) = x.eq(y)? {
                        return Ok(Value::Boolean(false));
                    }
                }

                Ok(Value::Boolean(true))
            }
            (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(*a == *b)),
            (Value::Number(a), Value::Number(b)) => {
                Ok(Value::Boolean((*a - *b).abs() <= Number::EPSILON))
            }
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a == b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn neq(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Array(_), Value::Array(_)) => match self.eq(other)? {
                Value::Boolean(equal) => Ok(Value::Boolean(!equal)),
                _ => unreachable!(),
            },
            (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(*a != *b)),
            (Value::Number(a), Value::Number(b)) => {
                Ok(Value::Boolean((*a - *b).abs() > Number::EPSILON))
            }
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a != b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn gt(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(*a > *b)),
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a > b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn gteq(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(*a >= *b)),
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a >= b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn lt(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(*a < *b)),
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a < b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn lteq(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Number(a), Value::Number(b)) => Ok(Value::Boolean(*a <= *b)),
            (Value::String(a), Value::String(b)) => Ok(Value::Boolean(a <= b)),
            _ => Err(ValueError::InvalidComparison(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn and(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(*a && *b)),
            _ => Err(ValueError::InvalidLogicalAnd(
                self.type_name(),
                other.type_name(),
            )),
        }
    }

    pub fn or(&self, other: &Value) -> Result<Self, ValueError> {
        match (self, other) {
            (Value::Boolean(a), Value::Boolean(b)) => Ok(Value::Boolean(*a || *b)),
            _ => Err(ValueError::InvalidLogicalOr(
                self.type_name(),
                other.type_name(),
            )),
        }
    }
}

impl From<&Literal> for Value {
    fn from(literal: &Literal) -> Self {
        match literal {
            Literal::Boolean(b, _) => Value::Boolean(*b),
            Literal::Number(n, _) => Value::Number(*n),
            Literal::String(s, _) => Value::String(s.clone()),
        }
    }
}

impl Display for Value {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Value::Array(elements) => {
                let elems_str = elements
                    .iter()
                    .map(|e| format!("{e}"))
                    .collect::<Vec<_>>()
                    .join(", ");
                write!(f, "[{elems_str}]")
            }
            Value::Boolean(b) => write!(f, "{b}"),
            Value::Closure { params, .. } => {
                let params_str = params
                    .iter()
                    .map(|p| format!("{}: {:?}", p.name, p.type_id))
                    .collect::<Vec<_>>()
                    .join(", ");
                write!(f, "|{params_str}| {{...}}")
            }
            Value::Function { name, params, .. } => {
                let params_str = params
                    .iter()
                    .map(|p| format!("{}: {:?}", p.name, p.type_id))
                    .collect::<Vec<_>>()
                    .join(", ");
                write!(f, "fn {name}({params_str})")
            }
            Value::Group {
                id,
                name,
                members,
                connections,
                schema,
            } => {
                let ports: Vec<String> = schema
                    .ports
                    .iter()
                    .map(|(p, d)| format!("{d} {p}"))
                    .collect();
                let mut member_names: Vec<&String> = members.keys().collect();
                member_names.sort();
                let conns: Vec<String> = connections
                    .iter()
                    .map(|c| {
                        format!(
                            "#{}.{} -> #{}.{}",
                            c.from_node_id, c.from_port, c.to_node_id, c.to_port
                        )
                    })
                    .collect();
                write!(
                    f,
                    "group({name}#{id}, ports=[{}], members=[{}], connections=[{}])",
                    ports.join(", "),
                    member_names
                        .iter()
                        .map(|s| s.as_str())
                        .collect::<Vec<_>>()
                        .join(", "),
                    conns.join(", "),
                )
            }
            Value::Node {
                id, name, schema, ..
            } => write!(f, "node {}({name}#{id})", schema.name),
            Value::Number(n) => write!(f, "{n}"),
            Value::NodeType(schema) => write!(f, "node type {}", schema.name),
            Value::PortRef {
                node_name,
                port_name,
                ..
            } => write!(f, "&{node_name}.{port_name}"),
            Value::Range {
                start,
                end,
                inclusive,
            } => {
                if *inclusive {
                    write!(f, "{start}..={end}")
                } else {
                    write!(f, "{start}..{end}")
                }
            }
            Value::Ref { name, .. } => write!(f, "&{name}"),
            Value::String(s) => write!(f, "{s}"),
            Value::Unit => write!(f, "()"),
            Value::Uninitialized => write!(f, "<uninitialized>"),
        }
    }
}
