use scip::types::Relationship;

use crate::core::schema::SchemaModel;

use super::super::symbol::to_scip_symbol;

pub(super) fn build_column_relationships(
    col: &crate::core::schema::ColumnDef,
    model: &SchemaModel,
) -> Vec<Relationship> {
    let mut rels = Vec::new();
    if let Some(ref type_qn) = col.type_ref {
        if model.types.contains_key(type_qn) {
            rels.push(type_def_rel(to_scip_symbol(&type_qn.to_string(), model)));
        }
    }
    if let Some((ref_table, ref_col)) = &col.fk_target {
        let fk_sym = to_scip_symbol(&format!("{ref_table}#{ref_col}"), model);
        rels.push(ref_rel(fk_sym, true));
    }
    rels
}

pub(super) fn build_function_relationships(
    func: &crate::core::schema::FunctionDef,
    model: &SchemaModel,
) -> Vec<Relationship> {
    let mut rels = Vec::new();
    if let Some(ref rt) = func.return_type {
        if model.types.contains_key(rt) {
            rels.push(type_def_rel(to_scip_symbol(&rt.to_string(), model)));
        }
    }
    for pt in &func.param_types {
        if model.types.contains_key(pt) {
            rels.push(type_def_rel(to_scip_symbol(&pt.to_string(), model)));
        }
    }
    rels
}

pub(super) fn build_view_relationships(
    view: &crate::core::schema::ViewDef,
    model: &SchemaModel,
) -> Vec<Relationship> {
    view.source_tables
        .iter()
        .filter(|t| model.tables.contains_key(*t) || model.views.contains_key(*t))
        .map(|t| ref_rel(to_scip_symbol(&t.to_string(), model), false))
        .collect()
}

pub(super) fn build_trigger_relationships(
    def: &crate::core::schema::TriggerDef,
    model: &SchemaModel,
) -> Vec<Relationship> {
    let mut rels = vec![ref_rel(
        to_scip_symbol(&def.target.to_string(), model),
        false,
    )];
    if let Some(ref func_qn) = def.func {
        if model.functions.contains_key(func_qn) {
            rels.push(ref_rel(to_scip_symbol(&func_qn.to_string(), model), false));
        }
    }
    rels
}

pub(super) fn type_def_rel(symbol: String) -> Relationship {
    let mut r = Relationship::new();
    r.symbol = symbol;
    r.is_type_definition = true;
    r
}

pub(super) fn ref_rel(symbol: String, and_type_def: bool) -> Relationship {
    let mut r = Relationship::new();
    r.symbol = symbol;
    r.is_reference = true;
    r.is_type_definition = and_type_def;
    r
}
