#!/usr/bin/env python3
"""
A bowtie runner which validates the requests and responses, before responding.
"""
from dataclasses import dataclass
from pathlib import Path
import io
import json
import os
import sys

from referencing.jsonschema import EMPTY_REGISTRY, specification_with
import jsonschema.validators
import referencing_loaders as loaders

SCHEMA_PATH = Path(os.environ.get("BOWTIE_SCHEMAS", "/schemas"))

_schemas = loaders.from_path(SCHEMA_PATH)
_REGISTRY = EMPTY_REGISTRY.with_resources(_schemas).crawl()
ROOT_URI = "tag:bowtie.report,2023:ihop"
METASCHEMA_ID = _REGISTRY.contents(ROOT_URI)["$schema"]
SPECIFICATION = specification_with(METASCHEMA_ID)
CURRENT_DIALECT = SPECIFICATION.create_resource(  # FIXME: wrong spec...
    {
        "$id": "tag:bowtie.report,2023:ihop:__dialect__",
        "$ref": METASCHEMA_ID,
    },
)
REGISTRY = CURRENT_DIALECT @ _REGISTRY

Validator = jsonschema.validators.validator_for(METASCHEMA_ID)


@dataclass
class Runner:
    _started: bool = False
    _stdout: io.TextIOWrapper = sys.stdout
    _stderr: io.TextIOWrapper = sys.stderr

    def run(self, stdin=sys.stdin):
        for line in stdin:
            each = json.loads(line)
            cmd = each["cmd"]
            uri = f"tag:bowtie.report,2023:ihop:command:{cmd}"
            request_validator = Validator({"$ref": uri}, registry=REGISTRY)
            for error in request_validator.iter_errors(each):
                self._stderr.write(error.message)
                self._stderr.write("\n")
            del each["cmd"]
            response_validator = Validator(
                {"$ref": f"{uri}#response"},
                registry=REGISTRY,
            )
            response = getattr(self, f"cmd_{cmd}")(**each)
            for error in response_validator.iter_errors(response):
                self._stderr.write(error.message)
                self._stderr.write("\n")
            self._stdout.write(f"{json.dumps(response)}\n")
            self._stdout.flush()

    def cmd_start(self, version):
        assert version == 1

        self._started = True
        return dict(
            version=1,
            implementation=dict(
                language="python",
                name="lintsonschema",
                homepage="https://github.com/bowtie-json-schema/bowtie",
                issues="https://github.com/bowtie-json-schema/bowtie/issues",
                source="https://github.com/bowtie-json-schema/bowtie",
                dialects=[
                    "https://json-schema.org/draft/2020-12/schema",
                    "https://json-schema.org/draft/2019-09/schema",
                    "http://json-schema.org/draft-07/schema#",
                    "http://json-schema.org/draft-06/schema#",
                    "http://json-schema.org/draft-04/schema#",
                    "http://json-schema.org/draft-03/schema#",
                ],
            ),
        )

    def cmd_dialect(self, dialect):
        assert self._started, "Not started!"
        return dict(ok=True)

    def cmd_run(self, case, seq):
        assert self._started, "Not started!"

        results = [
            {"valid": test.get("valid", True)} for test in case["tests"]
        ]
        return dict(seq=seq, results=results)

    def cmd_stop(self):
        assert self._started, "Not started!"

        sys.exit(0)


Runner().run()
