# -*- coding: utf-8 -*-

import re
import unittest

from pybel.constants import DIRTY
from pybel.parser import ConceptParser
from pybel.parser.exc import MissingNamespaceRegexWarning, NakedNameWarning


class _ParserMixin(unittest.TestCase):
    def setUp(self):
        self.namespace_to_term = {
            'A': {
                (None, '1'): 'P',
                (None, '2'): 'P',
                (None, '3'): 'P',
            },
            'B': {
                (None, '4'): 'P',
                (None, '5'): 'P',
                (None, '6'): 'P',
            }
        }


class TestConceptEnumerated(_ParserMixin):
    def setUp(self):
        super().setUp()
        self.parser = ConceptParser(namespace_to_term_to_encoding=self.namespace_to_term)

    def test_valid_1(self):
        s = 'A:3'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertEqual('A', result['namespace'])
        self.assertEqual('3', result['name'])

    def test_valid_2(self):
        s = 'A:"3"'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertEqual('A', result['namespace'])
        self.assertEqual('3', result['name'])

    def test_invalid_1(self):
        s = 'C:4'
        with self.assertRaises(Exception):
            self.parser.parseString(s)

    def test_invalid_2(self):
        s = 'A:4'
        with self.assertRaises(Exception):
            self.parser.parseString(s)

    def test_invalid_3(self):
        s = 'bare'
        with self.assertRaises(NakedNameWarning):
            self.parser.parseString(s)

    def test_invalid_4(self):
        s = '"quoted"'
        with self.assertRaises(NakedNameWarning):
            self.parser.parseString(s)


class TestConceptParserDefault(_ParserMixin):
    """Tests where the concept parser allows an enumerated list of unqualified entities."""

    def setUp(self):
        super().setUp()
        default_namespace = {'X', 'Y', 'W Z'}
        self.parser = ConceptParser(
            namespace_to_term_to_encoding=self.namespace_to_term,
            default_namespace=default_namespace,
        )

    def test_valid_1(self):
        s = 'A:3'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual('A', result['namespace'])
        self.assertEqual('3', result['name'])

    def test_valid_2(self):
        s = 'X'
        result = self.parser.parseString(s)

        self.assertIn('name', result)
        self.assertEqual('X', result['name'])

    def test_valid_3(self):
        s = '"W Z"'
        result = self.parser.parseString(s)

        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual('W Z', result['name'])

    def test_not_in_defaultNs(self):
        s = 'D'
        with self.assertRaises(Exception):
            self.parser.parseString(s)


class TestConceptParserLenient(_ParserMixin):
    """Tests where naked names are allowed."""

    def setUp(self):
        super().setUp()
        self.parser = ConceptParser(
            namespace_to_term_to_encoding=self.namespace_to_term,
            allow_naked_names=True,
        )

    def test_valid_1(self):
        s = 'A:3'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual('A', result['namespace'])
        self.assertEqual('3', result['name'])

    def test_valid_2(self):
        s = 'A:"3"'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual('A', result['namespace'])
        self.assertEqual('3', result['name'])

    def test_invalid_1(self):
        s = 'C:4'
        with self.assertRaises(Exception):
            self.parser.parseString(s)

    def test_invalid_2(self):
        s = 'A:4'
        with self.assertRaises(Exception):
            self.parser.parseString(s)

    def test_not_invalid_3(self):
        s = 'bare'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual(DIRTY, result['namespace'])
        self.assertEqual('bare', result['name'])

    def test_not_invalid_4(self):
        s = '"quoted"'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual(DIRTY, result['namespace'])
        self.assertEqual('quoted', result['name'])


class TestConceptParserRegex(unittest.TestCase):
    """Tests for regular expression parsing"""

    def setUp(self) -> None:
        self.parser = ConceptParser(namespace_to_pattern={'hgnc': re.compile(r'\d+')})
        self.assertEqual({}, self.parser.namespace_to_identifier_to_encoding)
        self.assertEqual({}, self.parser.namespace_to_name_to_encoding)

    def test_valid(self):
        s = 'hgnc:391'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertNotIn('identifier', result)
        self.assertEqual('hgnc', result['namespace'])
        self.assertEqual('391', result['name'])

    def test_invalid(self):
        """Test invalid BEL term."""
        s = 'hgnc:AKT1'
        with self.assertRaises(MissingNamespaceRegexWarning):
            result = self.parser.parseString(s)
            print(result.asDict())

    def test_valid_obo(self):
        """Test parsing an identifier that has a name."""
        s = 'hgnc:391 ! AKT1'
        result = self.parser.parseString(s)

        self.assertIn('namespace', result)
        self.assertIn('name', result)
        self.assertIn('identifier', result)
        self.assertEqual('hgnc', result['namespace'])
        self.assertEqual('AKT1', result['name'])
        self.assertEqual('391', result['identifier'])

    def test_invalid_obo(self):
        """Test parsing an OBO-style identifier where the identifier and name are switched."""
        s = 'hgnc:AKT1 ! 391'
        with self.assertRaises(MissingNamespaceRegexWarning):
            result = self.parser.parseString(s)
            print(result.asDict())
