//! Bidirectional relationship resolution
//!
//! Handles relationships that can be declared from either participant's
//! perspective, merging self/other blocks and validating consistency.

use std::collections::HashMap;

use crate::{
    resolve::{
        ResolveError,
        Result,
    },
    syntax::ast::{
        Declaration,
        Field,
        File,
        Participant,
        Relationship,
    },
};

/// A relationship key that's order-independent
/// (Martha, David) and (David, Martha) map to the same key
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct RelationshipKey {
    participants: Vec<String>,
    name: String,
}

impl RelationshipKey {
    fn new(mut participants: Vec<String>, name: String) -> Self {
        // Sort participants to make key order-independent
        participants.sort();
        Self { participants, name }
    }
}

/// Information about a relationship declaration
#[derive(Debug, Clone)]
struct RelationshipDecl {
    relationship: Relationship,
}

/// Resolved bidirectional relationship
#[derive(Debug, Clone)]
pub struct ResolvedRelationship {
    pub name: String,
    pub participants: Vec<Participant>,
    pub fields: Vec<Field>,
    /// Merged self/other blocks for each participant
    pub participant_fields: Vec<ParticipantFields>,
}

#[derive(Debug, Clone)]
pub struct ParticipantFields {
    pub participant_name: Vec<String>,
    pub role: Option<String>,
    /// Fields from this participant's block
    pub fields: Vec<Field>,
}

/// Resolve bidirectional relationships in a file
pub fn resolve_relationships(file: &File) -> Result<Vec<ResolvedRelationship>> {
    // Group relationships by key
    let mut relationship_groups: HashMap<RelationshipKey, Vec<RelationshipDecl>> = HashMap::new();

    for decl in &file.declarations {
        if let Declaration::Relationship(rel) = decl {
            // Extract participant names
            let participant_names: Vec<String> =
                rel.participants.iter().map(|p| p.name.join("::")).collect();

            let key = RelationshipKey::new(participant_names, rel.name.clone());

            relationship_groups
                .entry(key)
                .or_default()
                .push(RelationshipDecl {
                    relationship: rel.clone(),
                });
        }
    }

    // Merge grouped relationships
    let mut resolved = Vec::new();
    for (key, decls) in relationship_groups {
        let merged = merge_relationship_declarations(&key, decls)?;
        resolved.push(merged);
    }

    Ok(resolved)
}

/// Merge multiple declarations of the same relationship
fn merge_relationship_declarations(
    key: &RelationshipKey,
    decls: Vec<RelationshipDecl>,
) -> Result<ResolvedRelationship> {
    if decls.is_empty() {
        return Err(ResolveError::ValidationError {
            message: "Empty relationship group".to_string(),
            help: Some("This is an internal error - relationship groups should never be empty. Please report this as a bug.".to_string()),
        });
    }

    // Start with the first declaration
    let base = &decls[0].relationship;
    let mut participant_fields: Vec<ParticipantFields> = base
        .participants
        .iter()
        .map(|p| ParticipantFields {
            participant_name: p.name.clone(),
            role: p.role.clone(),
            fields: p.fields.clone(),
        })
        .collect();

    // Merge shared fields (fields outside participant blocks)
    let mut merged_fields = base.fields.clone();

    // Merge additional declarations
    for decl in decls.iter().skip(1) {
        // Merge participant fields
        for participant in &decl.relationship.participants {
            // Find this participant in our merged list
            if let Some(pf_idx) = participant_fields
                .iter()
                .position(|pf| pf.participant_name == participant.name)
            {
                // Merge fields for this participant
                merge_fields(
                    &mut participant_fields[pf_idx].fields,
                    participant.fields.clone(),
                )?;
            }
        }

        // Merge shared relationship fields
        merge_fields(&mut merged_fields, decl.relationship.fields.clone())?;
    }

    Ok(ResolvedRelationship {
        name: key.name.clone(),
        participants: base.participants.clone(),
        fields: merged_fields,
        participant_fields,
    })
}

/// Merge field lists, detecting conflicts
fn merge_fields(target: &mut Vec<Field>, source: Vec<Field>) -> Result<()> {
    for new_field in source {
        // Check if field already exists
        if let Some(existing) = target.iter().find(|f| f.name == new_field.name) {
            // Fields must have the same value
            if existing.value != new_field.value {
                return Err(ResolveError::ValidationError {
                    message: format!(
                        "Conflicting values for field '{}' in relationship",
                        new_field.name
                    ),
                    help: Some(format!(
                        "The field '{}' has different values in different declarations of the same relationship. Make sure all declarations of this relationship use the same value for shared fields.",
                        new_field.name
                    )),
                });
            }
            // Same value, no need to add again
        } else {
            // New field, add it
            target.push(new_field);
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::syntax::ast::{
        Span,
        Value,
    };

    fn make_participant(name: &str, role: Option<&str>) -> Participant {
        Participant {
            name: vec![name.to_string()],
            role: role.map(|s| s.to_string()),
            fields: vec![],
            span: Span::new(0, 10),
        }
    }

    fn make_field(name: &str, value: i64) -> Field {
        Field {
            name: name.to_string(),
            value: Value::Number(value),
            span: Span::new(0, 10),
        }
    }

    #[test]
    fn test_relationship_key_order_independent() {
        let key1 = RelationshipKey::new(
            vec!["Martha".to_string(), "David".to_string()],
            "Marriage".to_string(),
        );
        let key2 = RelationshipKey::new(
            vec!["David".to_string(), "Martha".to_string()],
            "Marriage".to_string(),
        );
        assert_eq!(key1, key2);
    }

    #[test]
    fn test_single_relationship_declaration() {
        let file = File {
            declarations: vec![Declaration::Relationship(Relationship {
                name: "Friendship".to_string(),
                participants: vec![
                    make_participant("Alice", None),
                    make_participant("Bob", None),
                ],
                fields: vec![make_field("bond", 80)],
                span: Span::new(0, 10),
            })],
        };

        let resolved = resolve_relationships(&file).unwrap();
        assert_eq!(resolved.len(), 1);
        assert_eq!(resolved[0].name, "Friendship");
        assert_eq!(resolved[0].participants.len(), 2);
    }

    #[test]
    fn test_relationship_merge() {
        let mut martha_participant = make_participant("Martha", Some("spouse"));
        martha_participant.fields = vec![make_field("commitment", 90)];

        let mut david_participant = make_participant("David", Some("spouse"));
        david_participant.fields = vec![make_field("trust", 85)];

        let file = File {
            declarations: vec![
                Declaration::Relationship(Relationship {
                    name: "Marriage".to_string(),
                    participants: vec![
                        martha_participant.clone(),
                        make_participant("David", Some("spouse")),
                    ],
                    fields: vec![],
                    span: Span::new(0, 10),
                }),
                Declaration::Relationship(Relationship {
                    name: "Marriage".to_string(),
                    participants: vec![
                        david_participant.clone(),
                        make_participant("Martha", Some("spouse")),
                    ],
                    fields: vec![],
                    span: Span::new(20, 30),
                }),
            ],
        };

        let resolved = resolve_relationships(&file).unwrap();
        assert_eq!(resolved.len(), 1);
        assert_eq!(resolved[0].name, "Marriage");
    }

    #[test]
    fn test_conflicting_field_values() {
        let mut p1 = make_participant("Alice", None);
        p1.fields = vec![make_field("bond", 80)];

        let mut p2 = make_participant("Alice", None);
        p2.fields = vec![make_field("bond", 90)]; // Different value

        let file = File {
            declarations: vec![
                Declaration::Relationship(Relationship {
                    name: "Test".to_string(),
                    participants: vec![p1, make_participant("Bob", None)],
                    fields: vec![],
                    span: Span::new(0, 10),
                }),
                Declaration::Relationship(Relationship {
                    name: "Test".to_string(),
                    participants: vec![p2, make_participant("Bob", None)],
                    fields: vec![],
                    span: Span::new(20, 30),
                }),
            ],
        };

        let result = resolve_relationships(&file);
        assert!(result.is_err());
    }
}
