use crate::Value;
use std::ops::{Index, IndexMut};

impl Index<usize> for Value {
    type Output = Value;

    fn index(&self, index: usize) -> &Value {
        match self {
            Value::Array(arr) => &arr[index],
            Value::Table(_) => &Value::Null,
            Value::Ext(_, ext) => {
                return ext.index(index);
            }
            _ => &Value::Null,
        }
    }
}

impl IndexMut<usize> for Value {
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        match self {
            Value::Array(arr) => &mut arr[index],
            Value::Table(t) => {
                let len = t.rows.len();
                if index >= len {
                    panic!("row index out of bounds: the len is {} but the index is {}", len, index);
                }
                // Table does not support direct mutable element indexing.
                // Use Table's row methods to modify data.
                panic!("table does not support mutable indexing, use Table methods");
            }
            Value::Ext(_, ext) => {
                return ext.index_mut(index);
            }
            _ => {
                panic!("not an array or table!")
            }
        }
    }
}

impl Index<&str> for Value {
    type Output = Value;
    fn index(&self, index: &str) -> &Self::Output {
        match self {
            Value::Map(m) => m.index(index),
            Value::Table(t) => {
                let _col_idx = t.columns.iter().position(|col| col.as_str() == Some(index));
                &Value::Null
            }
            Value::Ext(_, ext) => ext.index(index),
            _ => &Value::Null,
        }
    }
}

impl IndexMut<&str> for Value {
    fn index_mut(&mut self, index: &str) -> &mut Self::Output {
        match self {
            Value::Map(m) => m.index_mut(index),
            Value::Table(_) => {
                panic!("table does not support mutable indexing by string, use Table methods")
            }
            Value::Ext(_, ext) => {
                return ext.index_mut(index);
            }
            _ => {
                panic!("not map type")
            }
        }
    }
}


impl Index<Value> for Value {
    type Output = Value;

    fn index(&self, index: Value) -> &Self::Output {
        return match self {
            Value::Array(arr) => {
                let idx = index.as_u64().unwrap_or_default() as usize;
                arr.index(idx)
            }
            Value::Map(map) => {
                let s = index.as_str().unwrap_or_default();
                map.index(s)
            }
            Value::Table(t) => {
                if let Some(idx) = index.as_u64() {
                    let idx = idx as usize;
                    if idx < t.rows.len() {
                        // Return a reference to a static Null since we can't create
                        // a reference to a Value::Array(row) that lives long enough
                        &Value::Null
                    } else {
                        &Value::Null
                    }
                } else if let Some(s) = index.as_str() {
                    let col_idx = t.columns.iter().position(|col| col.as_str() == Some(s));
                    if col_idx.is_some() {
                        &Value::Null
                    } else {
                        &Value::Null
                    }
                } else {
                    &Value::Null
                }
            }
            Value::Ext(_, ext) => ext.index(index),
            _ => &Value::Null,
        };
    }
}


impl Index<&Value> for Value {
    type Output = Value;

    fn index(&self, index: &Value) -> &Self::Output {
        return match self {
            Value::Array(arr) => {
                let idx = index.as_u64().unwrap_or_default() as usize;
                arr.index(idx)
            }
            Value::Map(map) => {
                let s = index.as_str().unwrap_or_default();
                map.index(s)
            }
            Value::Table(t) => {
                if let Some(_idx) = index.as_u64() {
                    &Value::Null
                } else if let Some(s) = index.as_str() {
                    let _col_idx = t.columns.iter().position(|col| col.as_str() == Some(s));
                    &Value::Null
                } else {
                    &Value::Null
                }
            }
            Value::Ext(_, ext) => ext.index(index),
            _ => &Value::Null,
        };
    }
}


impl IndexMut<Value> for Value {
    fn index_mut(&mut self, index: Value) -> &mut Self::Output {
        match self {
            Value::Array(arr) => {
                let idx = index.as_u64().unwrap_or_default() as usize;
                arr.index_mut(idx)
            }
            Value::Map(map) => {
                let s = index.as_str().unwrap_or_default();
                map.index_mut(s)
            }
            Value::Table(_) => {
                panic!("table does not support mutable indexing, use Table methods")
            }
            Value::Ext(_, ext) => {
                ext.index_mut(index)
            }
            _ => {
                panic!("not map/array type")
            }
        }
    }
}


impl IndexMut<&Value> for Value {
    fn index_mut(&mut self, index: &Value) -> &mut Self::Output {
        match self {
            Value::Array(arr) => {
                let idx = index.as_u64().unwrap_or_default() as usize;
                arr.index_mut(idx)
            }
            Value::Map(map) => {
                let s = index.as_str().unwrap_or_default();
                map.index_mut(s)
            }
            Value::Table(_) => {
                panic!("table does not support mutable indexing, use Table methods")
            }
            Value::Ext(_, ext) => {
                ext.index_mut(index)
            }
            _ => {
                panic!("not map/array type")
            }
        }
    }
}

impl Value {
    pub fn insert(&mut self, key: Value, value: Value) -> Option<Value> {
        match self {
            Value::Null => None,
            Value::Bool(_) => None,
            Value::I32(_) => None,
            Value::I64(_) => None,
            Value::U32(_) => None,
            Value::U64(_) => None,
            Value::F32(_) => None,
            Value::F64(_) => None,
            Value::String(_) => None,
            Value::Binary(_) => None,
            Value::Array(arr) => {
                arr.insert(key.as_u64().unwrap_or_default() as usize, value);
                None
            }
            Value::Map(m) => m.insert(key, value),
            Value::Ext(_, m) => m.insert(key, value),
            Value::Table(_) => None,
        }
    }

    pub fn remove(&mut self, key: &Value) -> Value {
        match self {
            Value::Null => Value::Null,
            Value::Bool(_) => Value::Null,
            Value::I32(_) => Value::Null,
            Value::I64(_) => Value::Null,
            Value::U32(_) => Value::Null,
            Value::U64(_) => Value::Null,
            Value::F32(_) => Value::Null,
            Value::F64(_) => Value::Null,
            Value::String(_) => Value::Null,
            Value::Binary(_) => Value::Null,
            Value::Array(arr) => {
                let index = key.as_u64().unwrap_or_default() as usize;
                if index >= arr.len() {
                    return Value::Null;
                }
                arr.remove(index)
            }
            Value::Map(m) => m.remove(key),
            Value::Ext(_, e) => e.remove(key),
            Value::Table(_) => Value::Null,
        }
    }
}
