"""Generator functions for the stock app."""

from inspect import signature
from typing import Optional

from django.core.exceptions import ValidationError
from django.template import Context, Template

import common.models
import InvenTree.exceptions
import InvenTree.helpers


def generate_batch_code(**kwargs):
    """Generate a default 'batch code' for a new StockItem.

    By default, this uses the value of the 'STOCK_BATCH_CODE_TEMPLATE' setting (if configured),
    which can be passed through a simple template.

    Also, this function is exposed to the ValidationMixin plugin class,
    allowing custom plugins to be used to generate new batch code values.

    Various kwargs can be passed to the function, which will be passed through to the plugin functions.
    """
    # First, check if any plugins can generate batch codes
    from plugin import PluginMixinEnum, registry

    now = InvenTree.helpers.current_time()

    context = {
        'date': now,
        'year': now.year,
        'month': now.month,
        'day': now.day,
        'hour': now.hour,
        'minute': now.minute,
        'week': now.isocalendar()[1],
        **kwargs,
    }

    for plugin in registry.with_mixin(PluginMixinEnum.VALIDATION):
        generate = getattr(plugin, 'generate_batch_code', None)

        if not generate:
            continue

        # Check if the function signature accepts kwargs
        sig = signature(generate)

        if 'kwargs' in sig.parameters:
            # Pass the kwargs through to the plugin
            try:
                batch = generate(**context)
            except Exception:
                InvenTree.exceptions.log_error(
                    'generate_batch_code', plugin=plugin.slug
                )
                continue
        else:
            # Ignore the kwargs (legacy plugin)
            try:
                batch = generate()
            except Exception:
                InvenTree.exceptions.log_error(
                    'generate_batch_code', plugin=plugin.slug
                )
                continue

        # Return the first non-null value generated by a plugin
        if batch is not None:
            return batch

    # If we get to this point, no plugin was able to generate a new batch code
    batch_template = common.models.InvenTreeSetting.get_setting(
        'STOCK_BATCH_CODE_TEMPLATE', ''
    )

    return Template(batch_template).render(Context(context))


def generate_serial_number(part=None, quantity=1, **kwargs) -> Optional[str]:
    """Generate a default 'serial number' for a new StockItem."""
    quantity = quantity or 1

    if part is None:
        # Cannot generate a serial number without a part
        return None

    try:
        quantity = int(quantity)
    except Exception:
        raise ValidationError({'quantity': 'Invalid quantity value'})

    if quantity < 1:
        raise ValidationError({'quantity': 'Quantity must be greater than zero'})

    # If we are here, no plugins were available to generate a serial number
    # In this case, we will generate a simple serial number based on the provided part
    sn = part.get_latest_serial_number()

    serials = []

    # Generate the required quantity of serial numbers
    # Note that this call gets passed through to the plugin system
    while quantity > 0:
        sn = InvenTree.helpers.increment_serial_number(sn, part=part)

        # Exit if an empty or duplicated serial is generated
        if not sn or sn in serials:
            break

        serials.append(sn)
        quantity -= 1

    return ','.join(serials)
