import json as json_lib

from .errors import LoyaException
from .models import ModelFactory


class Parser:
    def parse(self, payload, *args, **kwargs):
        raise NotImplementedError


class RawParser(Parser):
    def __init__(self):
        pass

    def parse(self, payload, *args, **kwargs):
        return payload


class JSONParser(Parser):
    payload_format = "json"

    def parse(self, payload, **kwargs):
        if not payload:
            return None

        try:
            json = json_lib.loads(payload)
        except Exception as e:
            raise LoyaException(
                f"Failed to parse {self.payload_format} payload: {e}",
            )

        return json


class ModelParser(JSONParser):
    def __init__(self, model_factory=None):
        super().__init__()
        self.model_factory = model_factory or ModelFactory

    def parse(
        self, payload, *, api=None, payload_list=False, payload_type=None, **kwargs
    ):
        payload_type = kwargs.get("payload_type", payload_type)

        if payload_type is None:
            raise LoyaException("payload_type is required for ModelParser")

        try:
            model = getattr(self.model_factory, payload_type)
        except AttributeError:
            raise LoyaException(f"No model for this payload type: {payload_type}")

        json_data = super().parse(payload)

        if json_data is None:
            raise LoyaException("Failed to parse JSON payload")

        try:
            if payload_list:
                return model.parse_list(api, json_data)
            return model.parse(api, json_data)
        except Exception as e:
            raise LoyaException(f"Failed to parse model for {payload_type}: {e}") from e
