import typing
import enum
import io
import collections
import click
import json

from dataclasses import dataclass
from typing import List, Dict, TypeVar, Union, Generic, Optional

from ontobio.validation import metadata
from ontobio.io import assocparser
from ontobio.io import gafparser
from ontobio.io import assocwriter

"""
This module is for centralizing logic related to validating the example data in
GO Rules. Optional data can be included in rules that show exmaples of failing,
passing, or repair type rules for incoming data (either GAF, GPAD, or RDF).

This will first just support GAF for the first pass in all liklihood.

Relavent schema:
```
"examples":
  type: map
    required: false
    mapping:
      "pass":
        type: seq
        required: false
        sequence:
          - type: map
            mapping:
              "comment":
                type: str
                required: true
              "format":
                type: str
                required: true
                enum: ["rdf", "gaf", "gpad"]
              "input":
                type: str
                required: true
      "fail":
        type: seq
        required: false
        sequence:
          - type: map
            mapping:
              "comment":
                type: str
                required: true
              "format":
                type: str
                required: true
                enum: ["rdf", "gaf", "gpad"]
              "input":
                type: str
                required: true
      "repair":
        type: seq
        required: false
        sequence:
          - type: map
            mapping:
              "comment":
                type: str
                required: true
              "format":
                type: str
                required: true
                enum: ["rdf", "gaf", "gpad"]
              "input":
                type: str
                required: true
              "output":
                type: str
                required: true
```

"""
FormatType = enum.Enum("FormatType", ["RDF", "GAF", "GPAD"])
ExampleType = enum.Enum("ExampleType", {"REPAIR": "repair", "FAIL": "fail", "PASS": "pass"})

def format_from_string(format: str) -> Optional[FormatType]:
    if format == "rdf":
        return FormatType.RDF

    if format == "gaf":
        return FormatType.GAF

    if format == "gpad":
        return FormatType.GPAD

    return None

@dataclass
class RuleExample:
    rule_id: str
    example_type: ExampleType
    input: str
    format: FormatType
    expected: Union[str, bool]

    @classmethod
    def example_from_json(RuleExample, rule_json: Dict) -> List:
        """
        This constructs the set of examples to be run from a single GO rule.
        """
        # Returns List of RuleExample
        if "examples" not in rule_json:
            # Bail if we don't have any examples
            return []

        fail_examples = rule_json["examples"].get("fail", [])
        pass_examples = rule_json["examples"].get("pass", [])
        repair_examples = rule_json["examples"].get("repair", [])

        built_examples = [] # type: List[RuleExample]
        ruleid = rule_json["id"].lower().replace(":", "-")
        for ex in fail_examples:
            f = format_from_string(ex["format"]) # type: Optional[FormatType]
            # Expected is False since these are "fail" examples
            built_examples.append(RuleExample(ruleid, ExampleType.FAIL, ex["input"], f, False))

        for ex in pass_examples:
            f = format_from_string(ex["format"]) # type: Optional[FormatType]
            # Expected is True since these are "pass" examples
            built_examples.append(RuleExample(ruleid, ExampleType.PASS, ex["input"], f, True))

        for ex in repair_examples:
            f = format_from_string(ex["format"]) # type: Optional[FormatType]
            # Expected will come from the `output` field
            built_examples.append(RuleExample(ruleid, ExampleType.REPAIR, ex["input"], f, ex["output"]))

        return built_examples

@dataclass
class ValidationResult:
    example: RuleExample
    actual: Union[str, bool]
    success: bool
    reason: str

    def to_json(self) -> Dict:
        return {