"""The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself. It can be used as a template for developing third-party barcode plugins.

The data format is very simple, and maps directly to database objects,
via the "id" parameter.

Parsing an InvenTree barcode simply involves validating that the
references model objects actually exist in the database.
"""

import json

from django.utils.translation import gettext_lazy as _

from InvenTree.helpers import hash_barcode
from InvenTree.helpers_model import getModelsWithMixin
from InvenTree.models import InvenTreeBarcodeMixin
from plugin import InvenTreePlugin
from plugin.mixins import BarcodeMixin


class InvenTreeInternalBarcodePlugin(BarcodeMixin, InvenTreePlugin):
    """Builtin BarcodePlugin for matching and generating internal barcodes."""

    NAME = 'InvenTreeBarcode'
    TITLE = _('InvenTree Barcodes')
    DESCRIPTION = _('Provides native support for barcodes')
    VERSION = '2.0.0'
    AUTHOR = _('InvenTree contributors')

    @staticmethod
    def get_supported_barcode_models():
        """Returns a list of database models which support barcode functionality."""
        return getModelsWithMixin(InvenTreeBarcodeMixin)

    def format_matched_response(self, label, model, instance):
        """Format a response for the scanned data."""
        return {label: instance.format_matched_response()}

    def scan(self, barcode_data):
        """Scan a barcode against this plugin.

        Here we are looking for a dict object which contains a reference to a particular InvenTree database object
        """
        # Attempt to coerce the barcode data into a dict object
        # This is the internal barcode representation that InvenTree uses
        barcode_dict = None

        if type(barcode_data) is dict:
            barcode_dict = barcode_data
        elif type(barcode_data) is str:
            try:
                barcode_dict = json.loads(barcode_data)
            except json.JSONDecodeError:
                pass

        supported_models = self.get_supported_barcode_models()

        if barcode_dict is not None and type(barcode_dict) is dict:
            # Look for various matches. First good match will be returned
            for model in supported_models:
                label = model.barcode_model_type()

                if label in barcode_dict:
                    try:
                        pk = int(barcode_dict[label])
                        instance = model.objects.get(pk=pk)
                        return self.format_matched_response(label, model, instance)
                    except (ValueError, model.DoesNotExist):
                        pass

        # Create hash from raw barcode data
        barcode_hash = hash_barcode(barcode_data)

        # If no "direct" hits are found, look for assigned third-party barcodes
        for model in supported_models:
            label = model.barcode_model_type()

            instance = model.lookup_barcode(barcode_hash)

            if instance is not None:
                return self.format_matched_response(label, model, instance)
