# SPDX-FileCopyrightText: © 2023 Christian BUHTZ <c.buhtz@posteo.jp>
#
# SPDX-License-Identifier: GPL-3.0-only
#
# This file is part of the program "Hyperorg" which is released under GNU
# General Public License v3 (GPLv3).
# See folder LICENSES or go to <https://www.gnu.org/licenses/#GPL>.
"""Tests related to parser.py especially about properties, meta data and
comments in org files."""
import unittest
import datetime
from hyperorg import elements
from hyperorg import parser


class Properties(unittest.TestCase):
    """Test org node with properties"""

    def test_simple(self):
        """Simple properties"""
        content = [
            '#+foo',
            '#+bar and 42',
        ]
        sut = parser.OrgContentParser(content)

        self.assertEqual(len(sut.result()[1]), 0)
        # pylint: disable-next=protected-access
        self.assertEqual(sut._property_lines, content)


class Comments(unittest.TestCase):
    """Ignore comments while parsing"""

    def test_inline(self):
        """Inline comment"""
        content = [
            '# Inline comment',
            'foo',
            '# Inline comment',
            '',
            'bar',
        ]
        _, sut = parser.OrgContentParser(content).result()

        self.assertEqual(len(sut), 2)
        self.assertIsInstance(sut[0], elements.Paragraph)
        self.assertIsInstance(sut[1], elements.Paragraph)
        self.assertEqual(sut[0][0], 'foo')
        self.assertEqual(sut[1][0], 'bar')

    def test_block(self):
        """Simple figure."""
        content = [
            '#+BEGIN_COMMENT',
            'block comment',
            '#+END_COMMENT',
            'banana',
            '',
            '#+BEGIN_COMMENT',
            'block comment',
            '#+END_COMMENT',
            'strawberry',
        ]
        _, sut = parser.OrgContentParser(content).result()

        self.assertEqual(len(sut), 2)
        self.assertIsInstance(sut[0], elements.Paragraph)
        self.assertIsInstance(sut[1], elements.Paragraph)
        self.assertEqual(sut[0][0], 'banana')
        self.assertEqual(sut[1][0], 'strawberry')

    def test_block_not_case_sensitive(self):
        """Block comment mixed upper/lower case"""
        content = [
            '#+BEGIN_comment',
            'block comment',
            '#+end_COMMENT',
            'banana',
        ]
        _, sut = parser.OrgContentParser(content).result()

        self.assertEqual(len(sut), 1)
        self.assertIsInstance(sut[0], elements.Paragraph)
        self.assertEqual(sut[0][0], 'banana')

    def test_invalid_inline(self):
        """This is not a comment"""
        content = [
            '#invalid inline comment',
            '',
            'foo',
        ]
        _, sut = parser.OrgContentParser(content).result()

        self.assertEqual(len(sut), 2)
        self.assertIsInstance(sut[0], elements.Paragraph)
        self.assertIsInstance(sut[1], elements.Paragraph)
        self.assertEqual(sut[0][0], '#invalid inline comment')
        self.assertEqual(sut[1][0], 'foo')


class ParsingMeta(unittest.TestCase):
    """Parsing meta fields."""

    # pylint: disable=protected-access

    def test_title(self):
        """Title set via parse lines."""

        content = [
            '#+title: Foobar',
            '#+foo: Bar',
            'one'
        ]
        sut, elem = parser.OrgContentParser(content).result()

        # The title element (Heading) is not created by the parser but
        # by the Node calling the parser.
        self.assertEqual(sut, {'title': 'Foobar', 'foo': 'Bar'})

        # Just the paragraph
        self.assertEqual(len(elem), 1)

    def test_meta_at_file_beginning(self):
        """Parse meta (property) lines at beginning of a file."""

        content = [
            '#+title: Foobar',
            '#+date: 3022-08-06 18:23',
            '#+foo: bar',
            'one',
            'two',
            'three'
        ]

        meta, _ = parser.OrgContentParser(content).result()

        self.assertEqual(
            meta,
            {
                'title': 'Foobar',
                'date': datetime.datetime(3022, 8, 6, 18, 23),
                'foo': 'bar'
            }
        )

    def test_meta_in_file_body(self):
        """Possible meta (property) lines in the body of a file.

        The key point here is that "meta" lines in the file body are not
        parsed as meta data.
        """

        content = [
            '#+title: Foobar',
            '#+date: 3022-08-06 18:23',
            '#+foo: bar',
            'one',
            'two',
            '#+DOWNLOADED: url',
            'three',
            '#+filetags: foo'
        ]

        meta, elem = parser.OrgContentParser(content).result()

        self.assertEqual(
            meta,
            {
                'title': 'Foobar',
                'date': datetime.datetime(3022, 8, 6, 18, 23),
                'foo': 'bar',
                'filetags': ['foo'],
            }
        )

        self.assertEqual(len(elem[0]), 1)
        self.assertEqual(elem[0][0], 'one two three')

    def test_ignore_meta_missing_hash(self):
        """The '#' is missing."""
        content = ['+foo: bar', 'three']
        meta, elem = parser.OrgContentParser(content).result()
        self.assertEqual(meta, {})
        self.assertEqual(len(elem), 1)

    def test_ignore_meta_missing_blank(self):
        """Missing blank after ':'."""

        content = ['#+foo:bar', 'three']
        meta, elem = parser.OrgContentParser(content).result()

        self.assertEqual(len(elem), 1)
        self.assertEqual(meta, {})

    def test_filetags_complex(self):
        """Multiple filetags with multiple words"""

        content = ['#+filetags: One "Two zwei" Three']
        sut, _ = parser.OrgContentParser(content).result()

        self.assertEqual(sut, {'filetags': ['One', 'Two zwei', 'Three']})

    def test_filetags_simple(self):
        """One word filetag"""
        content = ['#+filetags: One']
        sut, _ = parser.OrgContentParser(content).result()

        self.assertEqual(sut, {'filetags': ['One']})


class ParsingMetaDate(unittest.TestCase):
    """Parsing date fields in meta data."""

    def test_date_without_brackets(self):
        """Date without brackets."""

        content = ['#+date: 2021-11-28 16:20']
        meta, _ = parser.OrgContentParser(content).result()

        self.assertEqual(meta,
                         {'date': datetime.datetime(2021, 11, 28, 16, 20)})

    def test_date_active(self):
        """Active dates with <>."""
        content = ['#+date: <2021-11-28 16:20>']
        meta, _ = parser.OrgContentParser(content).result()

        self.assertEqual(meta,
                         {'date': datetime.datetime(2021, 11, 28, 16, 20)})

    def test_date_inactive(self):
        """Inactive dates with []."""
        content = ['#+date: [2021-11-28 16:20]']
        meta, _ = parser.OrgContentParser(content).result()

        self.assertEqual(meta,
                         {'date': datetime.datetime(2021, 11, 28, 16, 20)})
