// Generated by Qleany v1.4.8 from feature_use_case.tera
use crate::DocumentStatsDto;
use anyhow::{Result, anyhow};
use common::database::QueryUnitOfWork;
use common::database::rope_helpers::block_content_via_store;
use common::direct_access::document::document_repository::DocumentRelationshipField;
use common::direct_access::frame::frame_repository::FrameRelationshipField;
use common::direct_access::root::root_repository::RootRelationshipField;
use common::entities::{Block, Document, Frame, Root};
use common::format_runs_query::get_block_images;
use common::types::{EntityId, ROOT_ENTITY_ID};

pub trait GetDocumentStatsUnitOfWorkFactoryTrait: Send + Sync {
    fn create(&self) -> Box<dyn GetDocumentStatsUnitOfWorkTrait>;
}

#[macros::uow_action(entity = "Root", action = "GetRO")]
#[macros::uow_action(entity = "Root", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Document", action = "GetRO")]
#[macros::uow_action(entity = "Document", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Frame", action = "GetMultiRO")]
#[macros::uow_action(entity = "Frame", action = "GetRelationshipRO")]
#[macros::uow_action(entity = "Block", action = "GetMultiRO")]
#[macros::uow_action(entity = "Block", action = "GetRelationshipRO")]
pub trait GetDocumentStatsUnitOfWorkTrait: QueryUnitOfWork {}

pub struct GetDocumentStatsUseCase {
    uow_factory: Box<dyn GetDocumentStatsUnitOfWorkFactoryTrait>,
}

impl GetDocumentStatsUseCase {
    pub fn new(uow_factory: Box<dyn GetDocumentStatsUnitOfWorkFactoryTrait>) -> Self {
        GetDocumentStatsUseCase { uow_factory }
    }

    pub fn execute(&mut self) -> Result<DocumentStatsDto> {
        let uow = self.uow_factory.create();
        uow.begin_transaction()?;

        // Get Root(1) -> Document
        let root = uow
            .get_root(&ROOT_ENTITY_ID)?
            .ok_or_else(|| anyhow!("Root entity not found"))?;

        let doc_ids = uow.get_root_relationship(&root.id, &RootRelationshipField::Document)?;
        let doc_id = *doc_ids
            .first()
            .ok_or_else(|| anyhow!("Root has no document"))?;

        let document = uow
            .get_document(&doc_id)?
            .ok_or_else(|| anyhow!("Document not found"))?;

        // Cached counts from Document
        let character_count = document.character_count;
        let block_count = document.block_count;

        // Frame count from relationship
        let frame_ids =
            uow.get_document_relationship(&doc_id, &DocumentRelationshipField::Frames)?;
        let frame_count = frame_ids.len() as i64;

        // List count from relationship
        let list_ids = uow.get_document_relationship(&doc_id, &DocumentRelationshipField::Lists)?;
        let list_count = list_ids.len() as i64;

        // Table count from relationship
        let table_ids =
            uow.get_document_relationship(&doc_id, &DocumentRelationshipField::Tables)?;
        let table_count = table_ids.len() as i64;

        // Get all frames to iterate blocks and elements
        let frames_opt = uow.get_frame_multi(&frame_ids)?;
        let frames: Vec<&Frame> = frames_opt.iter().filter_map(|f| f.as_ref()).collect();

        // Collect all block IDs from all frames
        let mut all_block_ids: Vec<EntityId> = Vec::new();
        for frame in &frames {
            let block_ids =
                uow.get_frame_relationship(&frame.id, &FrameRelationshipField::Blocks)?;
            all_block_ids.extend(block_ids);
        }

        // Get all blocks
        let blocks_opt = uow.get_block_multi(&all_block_ids)?;
        let blocks: Vec<&Block> = blocks_opt.iter().filter_map(|b| b.as_ref()).collect();

        // Word count: iterate all blocks, split content by whitespace
        // (content is read from the global rope via
        // `block_content_via_store`).
        let store = uow.store();
        let mut word_count: i64 = 0;
        for block in &blocks {
            let block_text = block_content_via_store(block, &store);
            word_count += block_text.split_whitespace().count() as i64;
        }

        // Image count: sum block_images entries across all blocks.
        let mut image_count: i64 = 0;
        for block in &blocks {
            image_count += get_block_images(&store, block.id).len() as i64;
        }

        uow.end_transaction()?;

        Ok(DocumentStatsDto {
            character_count,
            word_count,
            block_count,
            frame_count,
            image_count,
            list_count,
            table_count,
        })
    }
}
