import json
from pathlib import Path
import logging
from itertools import chain
from importlib import resources
from . import schemas

from jsonschema import Draft202012Validator
from referencing import Registry, Resource
from referencing.jsonschema import DRAFT202012


class JsonValidator:
    def __init__(self, core_schema, other_schemas):
        self._core_schema = json.load(core_schema.open())
        self._schema_dict = {self._core_schema["$id"]: self._core_schema}
        self._supporting_paths = []
        for thing in other_schemas:
            path = Path(thing)
            if path.is_dir():
                logging.debug(f"Searching {path} for schema files")
                self._supporting_paths.extend(path.rglob("*.schema.json"))
            else:
                logging.debug(f"Appending {path} as schema file")
                self._supporting_paths.append(path)
        logging.info(f"Supporting schema paths: {self._supporting_paths}")
        self._supporting_schemas = [json.load(p.open()) for p in self._supporting_paths]
        self._schema_dict = self._schema_dict | {
            s["$id"]: s for s in self._supporting_schemas
        }
        logging.info(f"Loaded schema IDs: {[k for k in self._schema_dict.keys()]}")
        # self._resolver = RefResolver(None,
        #                 referrer=None,
        #                 store=self._schema_dict)
        # logging.info("Created RefResolver")
        self._registry = Registry().with_resources(
            [
                (k, Resource(contents=v, specification=DRAFT202012))
                for k, v in self._schema_dict.items()
            ]
        )
        self._validator = Draft202012Validator(
            self._core_schema, registry=self._registry
        )
        logging.info("Created validator")

    def validate(self, instance_doc: str):
        errors = list(self._validator.iter_errors(instance_doc))
        return errors


class TS103976Validator(JsonValidator):
    def __init__(self):
        repo_path = resources.files(schemas).joinpath("dummy").parent  # Hack
        schema_dirs = [repo_path]
        core_schema = repo_path / "ts_103976.schema.json"
        JsonValidator.__init__(self, core_schema, schema_dirs)

class TS103120Validator(JsonValidator):
    def __init__(self):
        repo_path = resources.files(schemas).joinpath("dummy").parent  # Hack
        schema_dirs = [repo_path]
        core_schema = repo_path / "ts_103120_Core.schema.json"
        JsonValidator.__init__(self, core_schema, schema_dirs)
        request_fragment_schema = {
            "$ref": "ts_103120_Core_2019_10#/$defs/RequestPayload"
        }
        self._request_fragment_validator = Draft202012Validator(
            request_fragment_schema, registry=self._registry
        )
        response_fragment_schema = {
            "$ref": "ts_103120_Core_2019_10#/$defs/ResponsePayload"
        }
        self._response_fragment_validator = Draft202012Validator(
            response_fragment_schema, registry=self._registry
        )

    def expand_request_response_exception(self, ex):
        if list(ex.schema_path) == ["properties", "Payload", "oneOf"]:
            logging.info(
                "Error detected validating payload oneOf - attempting explicit validation..."
            )
            if "RequestPayload" in self.instance_doc["Payload"].keys():
                ret_list = list(
                    chain(
                        *[
                            self.expand_action_exception(x)
                            for x in self._request_fragment_validator.iter_errors(
                                self.instance_doc["Payload"]["RequestPayload"]
                            )
                        ]
                    )
                )
                for r in ret_list:
                    r.path = ex.path + r.path
                return ret_list
            elif "ResponsePayload" in self.instance_doc["Payload"].keys():
                ret_list = list(
                    chain(
                        *[
                            self.expand_action_exception(x)
                            for x in self._request_fragment_validator.iter_errors(
                                self.instance_doc["Payload"]["ResponsePayload"]
                            )
                        ]
                    )
                )
                for r in ret_list:
                    r.path = ex.path + r.path
                return ret_list
            else:
                logging.error(
                    "No RequestPayload or ResponsePayload found - is the Payload malformed?"
                )
                return [ex]
        else:
            return [ex]

    def expand_action_exception(self, ex):
        logging.error("Error detected in ActionRequests/ActionResponses")
        error_path = list(ex.schema_path)
        if error_path != [
            "properties",
            "ActionRequests",
            "properties",
            "ActionRequest",
            "items",
            "allOf",
            1,
            "oneOf",
        ] and error_path != [
            "properties",
            "ActionResponses",
            "properties",
            "ActionResponse",
            "items",
            "allOf",
            1,
            "oneOf",
        ]:
            logging.error("Error not in inner Request/Response allOf/oneOf constraint")
            return [ex]
        j = ex.instance
        j.pop(
            "ActionIdentifier"
        )  # Remove ActionIdentifier - one remaining key will be the verb
        verb = list(j.keys())[0]
        message = "Request" if error_path[1] == "ActionRequests" else "Response"
        v = Draft202012Validator(
            {"$ref": f"ts_103120_Core_2019_10#/$defs/{verb}{message}"},
            registry=self._registry,
        )
        ret_list = list(
            chain(*[self.expand_object_exception(x) for x in v.iter_errors(j[verb])])
        )
        for r in ret_list:
            r.path = ex.path + r.path
        return ret_list

    def expand_object_exception(self, ex):
        logging.error("Error detected in verb")
        # The final level of validation is for the actual HI1Object validation
        if list(ex.schema_path) != ["properties", "HI1Object", "oneOf"]:
            logging.error("Error not inside HI1Object")
            return [ex]
        object_type = ex.instance["@xsi:type"].split("}")[-1]
        object_ref = {
            "AuthorisationObject": "ts_103120_Authorisation_2020_09#/$defs/AuthorisationObject",
            "LITaskObject": "ts_103120_Task_2020_09#/$defs/LITaskObject",
            "LDTaskObject": "ts_103120_Task_2020_09#/$defs/LDTaskObject",
            "LPTaskObject": "ts_103120_Task_2020_09#/$defs/LPTaskObject",
            "DocumentObject": "ts_103120_Document_2020_09#/$defs/DocumentObject",
            "NotificationObject": "ts_103120_Notification_2016_02#/$defs/NotificationObject",
            "DeliveryObject": "ts_103120_Delivery_2019_10#/$defs/DeliveryObject",
            "TrafficPolicyObject": "ts_103120_TrafficPolicy_2022_07#/$defs/TrafficPolicyObject",
            "TrafficRuleObject": "ts_103120_TrafficPolicy_2022_07#/$defs/TrafficRuleObject",
        }[object_type]
        v = Draft202012Validator({"$ref": object_ref}, registry=self._registry)
        print(ex.instance)
        print(object_ref)
        return list(v.iter_errors(ex.instance))

    def validate(self, instance_doc: str):
        errors = JsonValidator.validate(self, instance_doc)
        self.instance_doc = instance_doc
        out_errors = list(
            chain(*[self.expand_request_response_exception(ex) for ex in errors])
        )
        return out_errors
        return errors
