#!/usr/bin/python3

from __future__ import absolute_import, print_function, unicode_literals

import sys
import os.path
import shutil
import unittest
import tempfile
from io import StringIO
from textwrap import dedent

sys.path.insert(0, os.path.realpath(
    os.path.join(os.path.dirname(__file__), "..")))

from xdiagnose.diagnostics import Diagnostic
from xdiagnose.config_update import (
    safe_backup,
    config_dict,
    config_update,
    )

class TestGrubFile(unittest.TestCase):
    def _setup_test_dir_with_file(self, filename, content):
        '''Creates a temporary directory with a single non-zero file'''
        temp_dir = tempfile.mkdtemp()
        temp_file = os.path.join(temp_dir, filename)
        f = open(temp_file, 'w')
        f.write(content)
        f.close()
        return temp_dir

    def _load_file(self, filename):
        f = open(filename, 'r')
        text = f.read()
        f.close()
        return text

    def test_backup_nonexistant_file(self):
        new_path = safe_backup('/dev/null/nonexistant')
        self.assertIsNone(new_path)

    def test_backup_existing_file(self):
        temp_file = tempfile.NamedTemporaryFile()
        new_path = safe_backup(temp_file.name)
        self.assertIsNotNone(new_path)
        self.assertNotEqual(temp_file.name, new_path)

    def test_backup_existing_file_already_backed_up(self):
        temp_file = tempfile.NamedTemporaryFile()
        new_path_1 = safe_backup(temp_file.name)
        new_path_2 = safe_backup(temp_file.name)
        self.assertNotEqual(new_path_1, new_path_2)
        self.assertNotEqual(temp_file.name, new_path_2)

    def test_backup_existing_symlink_file(self):
        temp_file = tempfile.NamedTemporaryFile()
        link_filename = temp_file.name+".link"
        os.symlink(temp_file.name, link_filename)

        new_path = safe_backup(link_filename)

        self.assertTrue(os.path.lexists(link_filename))
        self.assertTrue(os.path.exists(new_path))
        self.assertFalse(os.path.islink(new_path))

    def test_backup_existing_dir(self):
        # Setup
        filename = 'foo.txt'
        content = 'bar'
        temp_dir = self._setup_test_dir_with_file(filename, content)

        # Run code
        new_path = safe_backup(temp_dir)
        new_filename = os.path.join(temp_dir, filename)

        # Check results
        self.assertTrue(os.path.exists(new_path))
        self.assertTrue(os.path.exists(new_filename))
        self.assertNotEqual(new_path, temp_dir)
        self.assertEqual(content, self._load_file(new_filename))

        # Cleanup
        shutil.rmtree(temp_dir)
        shutil.rmtree(new_path)

    def test_backup_existing_dir_already_backed_up(self):
        # Setup
        filename = 'foo.txt'
        content = 'bar'
        temp_dir = self._setup_test_dir_with_file(filename, content)

        # Run code
        new_path_1 = safe_backup(temp_dir)
        new_path_2 = safe_backup(temp_dir)

        # Check results
        self.assertNotEqual(new_path_1, new_path_2)
        self.assertNotEqual(new_path_2, temp_dir)
        self.assertTrue(os.path.exists(new_path_2))

        # Cleanup
        shutil.rmtree(temp_dir)
        shutil.rmtree(new_path_1)
        shutil.rmtree(new_path_2)

    def test_load_sample(self):
        d = config_dict('tests/sample.conf')
        self.assertEqual(d, {
            'combo_1': 'c1,c2,c3',
            'combo_2': '"c1, c2, c3"',
            'param_1': 'p1',
            'param_2': '"p2"',
            'param_3': 'p 3',
            'param_4': 'p4',
            'param_5': 'True',
            'param_6': 'true',
            })

    def test_load_sample_grub(self):
        d = config_dict('tests/sample_grub.conf')
        self.assertEqual(d, {
            'GRUB_DEFAULT': '0',
            'GRUB_HIDDEN_TIMEOUT': '0',
            'GRUB_HIDDEN_TIMEOUT_QUIET': 'true',
            'GRUB_TIMEOUT': '10',
            'GRUB_DISTRIBUTOR': '`lsb_release -i -s 2> /dev/null || echo Debian`',
            'GRUB_CMDLINE_LINUX_DEFAULT': '""',
            'GRUB_CMDLINE_LINUX': '""',
            'GRUB_GFXPAYLOAD_LINUX': 'text',
            'AD_LINUX': 'text',
            })

    def test_save(self):
        filename = 'tests/sample.conf'
        sio = StringIO()
        config_update(filename, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=p1

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            """))
        sio.close()

    def test_save_overrides(self):
        filename = "tests/sample.conf"
        overrides = {'param_1': 'overridden'}
        sio = StringIO()
        output = config_update(filename, override_params=overrides, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=overridden

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            """))
        sio.close()

    def test_save_merges(self):
        filename = 'tests/sample.conf'
        merges = {'param_1': 'merged'}
        sio = StringIO()
        config_update(filename, merge_params=merges, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=p1 merged

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            """))
        sio.close()

    def test_save_overridden_and_merged(self):
        filename = 'tests/sample.conf'
        merges = {'param_1': 'merged'}
        overrides = {'param_1': 'overridden'}
        sio = StringIO()
        config_update(filename, merge_params=merges, override_params=overrides, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=overridden

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            """))
        sio.close()

    def test_save_overrides_with_merges(self):
        filename = 'tests/sample.conf'
        merges = {'param_1': 'merged', 'param_2': 'also_merged'}
        overrides = {'param_3': 'overridden', 'param_4': 'overridden_too'}
        sio = StringIO()
        config_update(filename, merge_params=merges, override_params=overrides, fileio=sio)
        self.assertEqual(dedent(
            """\
            param_1=p1 merged

            param_2="p2" also_merged
            param_3=overridden
            param_4=overridden_too
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            """), sio.getvalue())
        sio.close()

    def test_save_additions(self):
        filename = 'tests/sample.conf'
        merges = {'new_param_1': 'merged'}
        overrides = {'new_param_2': 'overridden'}
        sio = StringIO()
        config_update(filename, merge_params=merges, override_params=overrides, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=p1

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            new_param_1=merged
            new_param_2=overridden
            """))
        sio.close()

    def test_save_additions_of_disabled(self):
        filename = 'tests/sample.conf'
        merges = {'new_param_1': 'merged'}
        overrides = {'new_param_2': 'overridden'}
        sio = StringIO()
        config_update(filename, merge_params=merges, override_params=overrides, fileio=sio)
        self.assertEqual(sio.getvalue(), dedent(
            """\
            param_1=p1

            param_2="p2"
            param_3=p 3
            param_4 = p4
            param_5=True
            param_6=true

            combo_1=c1,c2,c3
            combo_2="c1, c2, c3"

            #new_param_1=disabled
            #new_param_2
            new_param_1=merged
            new_param_2=overridden
            """))
        sio.close()

if __name__ == '__main__':
    unittest.main()
