import asyncio

from inventory_handler import InventoryRepository
from inventory_handler.InventoryGenerators import Generator
from inventory_handler.InventoryTypes import Inventory, PropertyName, Properties, Property
from inventory_handler.InventoryUtils import logger


class InventoryService:
    """
    Provides inventory business logic like reading and updating properties.
    """

    def __init__(self, repo: InventoryRepository, generators=None):
        """
        Initialize the InventoryService with a repository and optional generators.

        :param repo: Instance managing file I/O and validation.
        :type repo: InventoryRepository
        :param generators: List of property value generators.
        :type generators: list[Generator] or None
        """
        if generators is None:
            generators = []
        self.repo: InventoryRepository = repo
        self.generators: list[Generator] = generators
        logger.debug(f"InventoryService initialized with {len(self.generators)} generator(s).")

    async def getInventory(self) -> Inventory:
        """
        Retrieve the current inventory from the model.

        Generates updated values before returning the full inventory.

        :return: The current inventory data.
        :rtype: Inventory
        """
        logger.info("Retrieving inventory...")
        await self.generateInventory()
        inventory = await self.repo.readAll()
        logger.info(f"Inventory retrieved with {len(inventory.get('properties', {}))} property(ies).")
        return inventory

    async def generateInventory(self):
        """
        Generate and persist the full inventory dictionary.

        For each registered generator, retrieves the current property,
        generates a new one, and saves all updated properties back to storage.
        """
        logger.info("Generating inventory...")
        await self.repo.readAll()

        async def fetchAndGenerate(generator: Generator) -> tuple[PropertyName, Property]:
            """
            Helper function to fetch current property and generate new value.

            :param generator: The generator used to compute a new property value.
            :type generator: Generator
            :return: A tuple of property name and the updated property.
            :rtype: tuple[PropertyName, Property]
            """
            logger.debug(f"Generating property: {generator.name.value}")
            base = await self.repo.read(generator.name)
            newProp = await generator.generate(base)
            logger.debug(f"Generated value for {generator.name.value}: {newProp['value']}")
            return generator.name, newProp

        results = await asyncio.gather(*[
            fetchAndGenerate(generator) for generator in self.generators
        ])

        properties: Properties = {name.value: value for name, value in results}
        inventory: Inventory = {
            "properties": properties
        }

        await self.repo.writeAll(inventory)
        logger.info(f"Inventory generation complete with {len(properties)} property(ies).")
