// Generated by Qleany v1.7.0 from entity_controller.tera

use super::{
    dtos::{CreateRelationshipDto, RelationshipDto, UpdateRelationshipDto},
    units_of_work::{RelationshipReadUoWFactory, RelationshipWriteUoWFactory},
};
use crate::RelationshipRelationshipDto;
use anyhow::{Ok, Result};
use common::direct_access::relationship::RelationshipRelationshipField;
use common::direct_access::use_cases;
use common::undo_redo::UndoRedoManager;
use common::{database::db_context::DbContext, event::EventHub, types::EntityId};
use std::sync::Arc;

pub fn create_orphan(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &CreateRelationshipDto,
) -> Result<RelationshipDto> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let mut uc = use_cases::UndoableCreateOrphanUseCase::new(uow_factory);
    let entity_in: common::entities::Relationship = entity.into();
    let result = uc.execute(&entity_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into())
}

pub fn create_orphan_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[CreateRelationshipDto],
) -> Result<Vec<RelationshipDto>> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entities_in: Vec<common::entities::Relationship> =
        entities.iter().map(|dto| dto.into()).collect();
    let mut uc = use_cases::UndoableCreateOrphanUseCase::new(uow_factory);
    let result = uc.execute_multi(&entities_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into_iter().map(|e| e.into()).collect())
}

pub fn create(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &CreateRelationshipDto,
    owner_id: EntityId,
    index: i32,
) -> Result<RelationshipDto> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entity_in: common::entities::Relationship = entity.into();
    let strategy = use_cases::OwnerStrategy::Appending;
    let mut uc = use_cases::UndoableCreateUseCase::new(uow_factory, strategy);
    let result = uc.execute(&entity_in, owner_id, index)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into())
}

pub fn create_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[CreateRelationshipDto],
    owner_id: EntityId,
    index: i32,
) -> Result<Vec<RelationshipDto>> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entities_in: Vec<common::entities::Relationship> =
        entities.iter().map(|dto| dto.into()).collect();
    let strategy = use_cases::OwnerStrategy::Appending;
    let mut uc = use_cases::UndoableCreateUseCase::new(uow_factory, strategy);
    let result = uc.execute_multi(&entities_in, owner_id, index)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into_iter().map(|e| e.into()).collect())
}

pub fn get(db_context: &DbContext, id: &EntityId) -> Result<Option<RelationshipDto>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc = use_cases::GetUseCase::new(uow_factory);
    Ok(uc.execute(id)?.map(|e| e.into()))
}

pub fn get_all(db_context: &DbContext) -> Result<Vec<RelationshipDto>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc = use_cases::GetUseCase::new(uow_factory);
    Ok(uc.execute_all()?.into_iter().map(|e| e.into()).collect())
}

pub fn get_multi(db_context: &DbContext, ids: &[EntityId]) -> Result<Vec<Option<RelationshipDto>>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc = use_cases::GetUseCase::new(uow_factory);
    Ok(uc
        .execute_multi(ids)?
        .into_iter()
        .map(|o| o.map(|e| e.into()))
        .collect())
}

pub fn update(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &UpdateRelationshipDto,
) -> Result<RelationshipDto> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entity_in: common::entities::Relationship = entity.into();
    let mut uc = use_cases::UndoableUpdateUseCase::new(uow_factory);
    let result = uc.execute(&entity_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into())
}

pub fn update_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[UpdateRelationshipDto],
) -> Result<Vec<RelationshipDto>> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entities_in: Vec<common::entities::Relationship> =
        entities.iter().map(|dto| dto.into()).collect();
    let mut uc = use_cases::UndoableUpdateUseCase::new(uow_factory);
    let result = uc.execute_multi(&entities_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into_iter().map(|e| e.into()).collect())
}

pub fn update_with_relationships(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entity: &RelationshipDto,
) -> Result<RelationshipDto> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entity_in: common::entities::Relationship = entity.into();
    let mut uc = use_cases::UndoableUpdateWithRelationshipsUseCase::new(uow_factory);
    let result = uc.execute(&entity_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into())
}

pub fn update_with_relationships_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    entities: &[RelationshipDto],
) -> Result<Vec<RelationshipDto>> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let entities_in: Vec<common::entities::Relationship> =
        entities.iter().map(|dto| dto.into()).collect();
    let mut uc = use_cases::UndoableUpdateWithRelationshipsUseCase::new(uow_factory);
    let result = uc.execute_multi(&entities_in)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result.into_iter().map(|e| e.into()).collect())
}

pub fn remove(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    id: &EntityId,
) -> Result<()> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let mut uc = use_cases::UndoableRemoveUseCase::new(uow_factory);
    uc.execute(id)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(())
}

pub fn remove_multi(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    ids: &[EntityId],
) -> Result<()> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let mut uc = use_cases::UndoableRemoveUseCase::new(uow_factory);
    uc.execute_multi(ids)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(())
}

pub fn get_relationship(
    db_context: &DbContext,
    id: &EntityId,
    field: &RelationshipRelationshipField,
) -> Result<Vec<EntityId>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc =
        use_cases::GetRelationshipUseCase::<RelationshipRelationshipField, _>::new(uow_factory);
    uc.execute(id, field)
}

pub fn get_relationship_many(
    db_context: &DbContext,
    ids: &[EntityId],
    field: &RelationshipRelationshipField,
) -> Result<std::collections::HashMap<EntityId, Vec<EntityId>>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc =
        use_cases::GetRelationshipManyUseCase::<RelationshipRelationshipField, _>::new(uow_factory);
    uc.execute(ids, field)
}

pub fn get_relationship_count(
    db_context: &DbContext,
    id: &EntityId,
    field: &RelationshipRelationshipField,
) -> Result<usize> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc = use_cases::GetRelationshipCountUseCase::<RelationshipRelationshipField, _>::new(
        uow_factory,
    );
    uc.execute(id, field)
}

pub fn get_relationship_in_range(
    db_context: &DbContext,
    id: &EntityId,
    field: &RelationshipRelationshipField,
    offset: usize,
    limit: usize,
) -> Result<Vec<EntityId>> {
    let uow_factory = RelationshipReadUoWFactory::new(db_context);
    let uc = use_cases::GetRelationshipInRangeUseCase::<RelationshipRelationshipField, _>::new(
        uow_factory,
    );
    uc.execute(id, field, offset, limit)
}

pub fn set_relationship(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    dto: &RelationshipRelationshipDto,
) -> Result<()> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let mut uc = use_cases::UndoableSetRelationshipUseCase::<RelationshipRelationshipField, _>::new(
        uow_factory,
    );
    uc.execute(&dto.id, &dto.field, dto.right_ids.as_slice())?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(())
}

#[allow(clippy::too_many_arguments)]
pub fn move_relationship(
    db_context: &DbContext,
    event_hub: &Arc<EventHub>,
    undo_redo_manager: &mut UndoRedoManager,
    stack_id: Option<u64>,
    id: &EntityId,
    field: &RelationshipRelationshipField,
    ids_to_move: &[EntityId],
    new_index: i32,
) -> Result<Vec<EntityId>> {
    let uow_factory = RelationshipWriteUoWFactory::new(db_context, event_hub);
    let mut uc =
        use_cases::UndoableMoveRelationshipUseCase::<RelationshipRelationshipField, _>::new(
            uow_factory,
        );
    let result = uc.execute(id, field, ids_to_move, new_index)?;
    undo_redo_manager.add_command_to_stack(Box::new(uc), stack_id)?;
    Ok(result)
}

#[cfg(test)]
mod tests {
    #![allow(dead_code)]
    #![allow(unused_imports)]

    use super::*;
    use crate::entity::dtos::CreateEntityDto;
    use crate::entity::entity_controller;
    use crate::root::dtos::CreateRootDto;
    use crate::root::root_controller;
    use crate::workspace::dtos::CreateWorkspaceDto;
    use crate::workspace::workspace_controller;
    use common::database::db_context::DbContext;
    use common::event::EventHub;
    use common::types::EntityId;
    use common::undo_redo::UndoRedoManager;
    use std::sync::Arc;

    struct TestContext {
        db: DbContext,
        hub: Arc<EventHub>,
        undo: UndoRedoManager,
    }

    impl TestContext {
        fn new() -> Self {
            let db = DbContext::new().expect("Failed to create in-memory DB");
            let hub = Arc::new(EventHub::new());
            let mut undo = UndoRedoManager::new();
            undo.set_event_hub(&hub);
            TestContext { db, hub, undo }
        }
    }

    /// Build the ownership chain and return the direct owner's id.
    fn create_owner_chain(ctx: &mut TestContext) -> EntityId {
        let root =
            root_controller::create_orphan(&ctx.db, &ctx.hub, &CreateRootDto::default()).unwrap();
        let workspace = workspace_controller::create(
            &ctx.db,
            &ctx.hub,
            &mut ctx.undo,
            None,
            &CreateWorkspaceDto::default(),
            root.id,
            -1,
        )
        .unwrap();
        let entity = entity_controller::create(
            &ctx.db,
            &ctx.hub,
            &mut ctx.undo,
            None,
            &CreateEntityDto::default(),
            workspace.id,
            -1,
        )
        .unwrap();
        entity.id
    }

    /// Create an entity using create_orphan.
    fn create_one(ctx: &mut TestContext) -> RelationshipDto {
        create_orphan(
            &ctx.db,
            &ctx.hub,
            &mut ctx.undo,
            None,
            &CreateRelationshipDto::default(),
        )
        .unwrap()
    }

    // -----------------------------------------------------------------------
    // create_orphan + get
    // -----------------------------------------------------------------------

    #[test]
    fn test_create_orphan_and_get() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        assert!(created.id > 0);

        let fetched = get(&ctx.db, &created.id).unwrap();
        assert!(fetched.is_some());
        assert_eq!(fetched.unwrap().id, created.id);
    }

    // -----------------------------------------------------------------------
    // get nonexistent
    // -----------------------------------------------------------------------

    #[test]
    fn test_get_nonexistent() {
        let ctx = TestContext::new();
        assert!(get(&ctx.db, &999999).unwrap().is_none());
    }

    // -----------------------------------------------------------------------
    // get_all
    // -----------------------------------------------------------------------

    #[test]
    fn test_get_all() {
        let mut ctx = TestContext::new();
        create_one(&mut ctx);
        let all = get_all(&ctx.db).unwrap();
        assert!(!all.is_empty());
    }

    // -----------------------------------------------------------------------
    // get_multi
    // -----------------------------------------------------------------------

    #[test]
    fn test_get_multi() {
        let mut ctx = TestContext::new();
        let a = create_one(&mut ctx);
        let results = get_multi(&ctx.db, &[a.id, 999999]).unwrap();
        assert_eq!(results.len(), 2);
        assert!(results[0].is_some());
        assert!(results[1].is_none());
    }

    // -----------------------------------------------------------------------
    // update
    // -----------------------------------------------------------------------

    #[test]
    fn test_update() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        let update_dto: UpdateRelationshipDto = created.into();
        let updated = update(&ctx.db, &ctx.hub, &mut ctx.undo, None, &update_dto).unwrap();
        assert_eq!(updated.id, update_dto.id);
    }

    // -----------------------------------------------------------------------
    // remove
    // -----------------------------------------------------------------------

    #[test]
    fn test_remove() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        remove(&ctx.db, &ctx.hub, &mut ctx.undo, None, &created.id).unwrap();
        assert!(get(&ctx.db, &created.id).unwrap().is_none());
    }

    // -----------------------------------------------------------------------
    // remove_multi
    // -----------------------------------------------------------------------

    #[test]
    fn test_remove_multi() {
        let mut ctx = TestContext::new();
        let a = create_one(&mut ctx);
        remove_multi(&ctx.db, &ctx.hub, &mut ctx.undo, None, &[a.id]).unwrap();
        assert!(get(&ctx.db, &a.id).unwrap().is_none());
    }

    // -----------------------------------------------------------------------
    // create with owner
    // -----------------------------------------------------------------------

    #[test]
    fn test_create_with_owner() {
        let mut ctx = TestContext::new();
        let owner_id = create_owner_chain(&mut ctx);
        let created = create(
            &ctx.db,
            &ctx.hub,
            &mut ctx.undo,
            None,
            &CreateRelationshipDto::default(),
            owner_id,
            -1,
        )
        .unwrap();
        assert!(created.id > 0);
        let fetched = get(&ctx.db, &created.id).unwrap();
        assert!(fetched.is_some());
    }

    // -----------------------------------------------------------------------
    // undo create_orphan
    // -----------------------------------------------------------------------

    #[test]
    fn test_create_orphan_undo() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        assert!(get(&ctx.db, &created.id).unwrap().is_some());
        ctx.undo.undo(None).unwrap();
        assert!(get(&ctx.db, &created.id).unwrap().is_none());
    }

    // -----------------------------------------------------------------------
    // undo remove
    // -----------------------------------------------------------------------

    #[test]
    fn test_remove_undo() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        remove(&ctx.db, &ctx.hub, &mut ctx.undo, None, &created.id).unwrap();
        assert!(get(&ctx.db, &created.id).unwrap().is_none());
        ctx.undo.undo(None).unwrap();
        assert!(get(&ctx.db, &created.id).unwrap().is_some());
    }

    // -----------------------------------------------------------------------
    // relationship: get default
    // -----------------------------------------------------------------------

    #[test]
    fn test_get_relationship_default() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        let rel_ids = get_relationship(
            &ctx.db,
            &created.id,
            &RelationshipRelationshipField::LeftEntity,
        )
        .unwrap();
        assert!(rel_ids.is_empty());
    }

    // -----------------------------------------------------------------------
    // relationship: count default
    // -----------------------------------------------------------------------

    #[test]
    fn test_get_relationship_count_default() {
        let mut ctx = TestContext::new();
        let created = create_one(&mut ctx);
        let count = get_relationship_count(
            &ctx.db,
            &created.id,
            &RelationshipRelationshipField::LeftEntity,
        )
        .unwrap();
        assert_eq!(count, 0);
    }
}
