use super::*;

impl SchemaValidator {
    pub(super) fn collect_names(&mut self) {
        for decl in &self.schema.declarations {
            match decl {
                Declaration::Model(model) => {
                    let name = model.name.value.clone();
                    if let Some(existing_span) = self.models.get(&name) {
                        self.errors.push_back(SchemaError::Validation(
                            format!(
                                "Duplicate model name '{}' (first defined at {})",
                                name, existing_span
                            ),
                            model.name.span,
                        ));
                    } else if self
                        .enums
                        .get(&name)
                        .or(self.composite_types.get(&name))
                        .is_some()
                    {
                        self.errors.push_back(SchemaError::Validation(
                            format!("Name '{}' is already used by an enum or type", name),
                            model.name.span,
                        ));
                    } else {
                        self.models.insert(name, model.name.span);
                    }
                }
                Declaration::Enum(enum_decl) => {
                    let name = enum_decl.name.value.clone();
                    if let Some(existing_span) = self.enums.get(&name) {
                        self.errors.push_back(SchemaError::Validation(
                            format!(
                                "Duplicate enum name '{}' (first defined at {})",
                                name, existing_span
                            ),
                            enum_decl.name.span,
                        ));
                    } else if self
                        .models
                        .get(&name)
                        .or(self.composite_types.get(&name))
                        .is_some()
                    {
                        self.errors.push_back(SchemaError::Validation(
                            format!("Name '{}' is already used by a model or type", name),
                            enum_decl.name.span,
                        ));
                    } else {
                        self.enums.insert(name, enum_decl.name.span);
                    }
                }
                Declaration::Type(type_decl) => {
                    let name = type_decl.name.value.clone();
                    if let Some(existing_span) = self.composite_types.get(&name) {
                        self.errors.push_back(SchemaError::Validation(
                            format!(
                                "Duplicate type name '{}' (first defined at {})",
                                name, existing_span
                            ),
                            type_decl.name.span,
                        ));
                    } else if self.models.get(&name).or(self.enums.get(&name)).is_some() {
                        self.errors.push_back(SchemaError::Validation(
                            format!("Name '{}' is already used by a model or enum", name),
                            type_decl.name.span,
                        ));
                    } else {
                        self.composite_types.insert(name, type_decl.name.span);
                    }
                }
                _ => {}
            }
        }
    }

    pub(super) fn check_physical_name_collisions(&mut self) {
        let mut table_names: HashMap<String, (String, Span)> = HashMap::new();
        for decl in &self.schema.declarations {
            if let Declaration::Model(model) = decl {
                let table_name = model.table_name().to_string();
                if let Some((existing_model, existing_span)) = table_names.get(&table_name) {
                    self.errors.push_back(SchemaError::Validation(
                        format!(
                            "Physical table name '{}' is used by both model '{}' and '{}' (first defined at {})",
                            table_name, model.name.value, existing_model, existing_span
                        ),
                        model.span,
                    ));
                } else {
                    table_names.insert(table_name, (model.name.value.clone(), model.span));
                }

                let mut column_names: HashMap<String, (String, Span)> = HashMap::new();
                for field in &model.fields {
                    let column_name = field.column_name().to_string();
                    if let Some((existing_field, existing_span)) = column_names.get(&column_name) {
                        self.errors.push_back(SchemaError::Validation(
                            format!(
                                "Physical column name '{}' in model '{}' is used by both field '{}' and '{}' (first defined at {})",
                                column_name, model.name.value, field.name.value, existing_field, existing_span
                            ),
                            field.span,
                        ));
                    } else {
                        column_names.insert(column_name, (field.name.value.clone(), field.span));
                    }
                }
            }
        }
    }
}
