// Copyright © 2024-2025 The µcad authors <info@ucad.xyz>
// SPDX-License-Identifier: AGPL-3.0-or-later

//! *Argument value list* evaluation entity.

use crate::{eval::*, src_ref::*, value::*};
use derive_more::{Deref, DerefMut};

/// Collection of *argument values* (e.g. `( x=1, y=2 )`).
///
/// Also provides methods to find a matching call
/// between it and a given *parameter list*.
#[derive(Clone, Default, Deref, DerefMut)]
pub struct ArgumentValueList {
    #[deref]
    #[deref_mut]
    map: Vec<(Identifier, ArgumentValue)>,
    src_ref: SrcRef,
}

impl ArgumentValueList {
    /// Create new [`ArgumentValueList`]
    pub fn new(map: Vec<(Identifier, ArgumentValue)>, src_ref: SrcRef) -> Self {
        Self { map, src_ref }
    }

    /// Return a single argument.
    ///
    /// Returns error if there is no or more than one argument available.
    pub fn get_single(&self) -> EvalResult<(&Identifier, &ArgumentValue)> {
        if self.map.len() == 1 {
            if let Some(a) = self.map.first() {
                return Ok((&a.0, &a.1));
            }
        }

        Err(EvalError::ArgumentCountMismatch {
            args: self.to_string(),
            expected: 1,
            found: self.map.len(),
        })
    }

    /// Get value by type
    pub fn get_by_type(&self, ty: &Type) -> Option<(&Identifier, &ArgumentValue)> {
        let arg = self.map.iter().find(|(_, arg)| arg.value.ty() == *ty);
        arg.map(|arg| (&arg.0, &arg.1))
    }
}

impl ValueAccess for ArgumentValueList {
    fn by_id(&self, id: &Identifier) -> Option<&Value> {
        self.map
            .iter()
            .find(|(i, _)| i == id)
            .map(|arg| &arg.1.value)
    }

    fn by_ty(&self, ty: &Type) -> Option<&Value> {
        self.get_by_type(ty).map(|(_, arg)| &arg.value)
    }
}

impl SrcReferrer for ArgumentValueList {
    fn src_ref(&self) -> SrcRef {
        self.src_ref.clone()
    }
}

impl std::fmt::Display for ArgumentValueList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", {
            let mut v = self
                .map
                .iter()
                .map(|(id, p)| format!("{id}{p}"))
                .collect::<Vec<_>>();
            v.sort();
            v.join(", ")
        })
    }
}

impl std::fmt::Debug for ArgumentValueList {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", {
            let mut v = self
                .map
                .iter()
                .map(|(id, av)| format!("{id:?}{av:?}"))
                .collect::<Vec<_>>();
            v.sort();
            v.join(", ")
        })
    }
}

impl FromIterator<(Identifier, ArgumentValue)> for ArgumentValueList {
    fn from_iter<T: IntoIterator<Item = (Identifier, ArgumentValue)>>(iter: T) -> Self {
        let map: Vec<_> = iter.into_iter().collect();
        Self {
            src_ref: SrcRef::merge_all(map.iter().map(|(_, v)| v.src_ref())),
            map,
        }
    }
}

#[test]
fn test_argument_value_debug() {
    let arg1 = ArgumentValue::new(
        Value::Target(Target::new("my::name1".into(), Some("my::target1".into()))),
        Some("id1".into()),
        SrcRef(None),
    );

    let arg2 = ArgumentValue::new(
        Value::Target(Target::new("my::name2".into(), None)),
        Some("id2".into()),
        SrcRef(None),
    );

    let mut args = ArgumentValueList::default();

    args.push(("id1".into(), arg1));
    args.push(("id2".into(), arg2));

    log::info!("{args}");
    log::info!("{args:?}");
}
