import logging
import os
from dataclasses import dataclass, field
from io import StringIO
from pathlib import Path
from typing import Collection, Tuple, Union, cast

import pytest
from _pytest.mark.structures import Mark, MarkDecorator, ParameterSet

import rdflib.plugins.sparql.algebra as algebra
import rdflib.plugins.sparql.parser as parser
from rdflib.plugins.sparql.algebra import translateAlgebra


@pytest.fixture
def data_path() -> Path:
    return Path(__file__).parent / "translate_algebra"


@dataclass
class AlgebraTest:
    id: str
    description: str
    marks: Union[MarkDecorator, Collection[Union[MarkDecorator, Mark]]] = field(
        default_factory=lambda: cast(Tuple[MarkDecorator], tuple())
    )

    @property
    def filename(self) -> str:
        return f"{self.id}.txt"

    def pytest_param(self) -> ParameterSet:
        return pytest.param(self, id=self.id, marks=self.marks)


def _format_query(query: str) -> str:
    buffer = StringIO()
    p = "{"
    q = "}"
    i = 0
    f = 1

    for e in query:
        if e in p:
            if not f:
                buffer.write("\n")
            buffer.write(" " * i + e)
            buffer.write("\n")
            i += 4
            f = 1
        elif e in q:
            if not f:
                buffer.write("\n")
            i -= 4
            f = 1
            buffer.write(" " * i + e)
            buffer.write("\n")
        else:
            if f:
                buffer.write(" " * i)
            buffer.write(e)
    return buffer.getvalue()


algebra_tests = [
    AlgebraTest(
        "test_functions__functional_forms",
        "Test if functional forms are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__functional_forms_not_exists",
        "Test if the not exists form is properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__functions_on_dates_and_time",
        "Test if functions on dates and time are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__functions_on_numerics",
        "Test if functions on numerics are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__functions_on_rdf_terms",
        "Test if functions on rdf terms are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__functions_on_strings",
        "Test if functions on strings are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_functions__hash_functions",
        "Test if hash functions are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_graph_patterns__aggregate_join",
        "Test if aggregate join including all aggregation functions"
        "are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_graph_patterns__bgp",
        "Test if basic graph patterns are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_graph_patterns__extend",
        'Test if "extend" (=Bind explicitly or implicitly in projection)'
        "gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_graph_patterns__filter",
        "Test if filter gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_graph_patterns__graph",
        'Test if "graph" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__group",
        'Test if "group" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__group_and_substr",
        'Test if a query with a variable that is used in the "GROUP BY" clause '
        'and in the SUBSTR function gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__group_and_nested_concat",
        'Test if a query with a nested concat expression in the select clause which '
        'uses a group variable gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__having",
        'Test if "having" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__join",
        'Test if "join" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__left_join",
        'Test if "left join" gets properly translated into "OPTIONAL {...}" in the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__minus",
        'Test if "minus" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_graph_patterns__union",
        'Test if "union" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_integration__complex_query1",
        "Test a query with multiple graph patterns and solution modifiers"
        "gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_operators__arithmetics",
        "Test if arithmetics are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_operators__conditional_and",
        'Test if "conditional ands (&&)" are properly translated into the query text.',
    ),
    AlgebraTest(
        "test_operators__conditional_or",
        'Test if "conditional ors (||)" are properly translated into the query text.',
    ),
    AlgebraTest(
        "test_operators__relational",
        "Test if relational expressions are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_operators__unary",
        "Test if unary expressions are properly translated into the query text.",
    ),
    AlgebraTest(
        "test_other__service2",
        'Test if "service" along with its service string is properly translated'
        "into the query text.",
    ),
    AlgebraTest(
        "test_other__values",
        'Test if "values" gets properly translated into the query text.',
    ),
    AlgebraTest(
        "test_property_path__alternative_path",
        "Test if an alternative path gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_property_path__inverse_path",
        "Test if an inverse path gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_property_path__negated_property_set",
        "Test if a negated property set gets properly translated into the query text.",
        pytest.mark.xfail(
            raises=TypeError, reason="fails with TypeError in translateAlgebra"
        ),
    ),
    AlgebraTest(
        "test_property_path__one_or_more_path",
        "Test if a oneOrMore path gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_property_path__sequence_path",
        "Test if a sequence path gets properly translated into the query text.",
    ),
    AlgebraTest(
        "test_property_path__zero_or_more_path",
        "Test if a zeroOrMore path gets properly translated into the query text.",
    ),
    AlgebraTest(