"""Tests for Rt - Python interface to Request Tracker :term:`API`"""

# ruff: noqa: S101, S105, S311

__license__ = """ Copyright (C) 2013 CZ.NIC, z.s.p.o.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
__docformat__ = 'reStructuredText en'
__authors__ = [
    '"Jiri Machalek" <jiri.machalek@nic.cz>',
]

import random
import string
import unittest

import requests.utils

import rt.rest1


class RtTestCase(unittest.TestCase):
    rt.DEBUG_MODE = True
    RT_VALID_CREDENTIALS = {
        'RT4.4 stable': {
            'url': 'http://localhost:8080/REST/1.0/',
            'support': {
                'default_login': 'root',
                'default_password': 'password',
            },
        },
        # HTTP timeout
        # 'RT4.6 dev': {
        #     'url': 'http://rt.easter-eggs.org/demos/4.6/REST/1.0',
        #     'admin': {
        #         'default_login': 'administrateur',
        #         'default_password': 'administrateur',
        #     },
        #     'john.foo': {
        #         'default_login': 'support',
        #         'default_password': 'support',
        #     }
        # },
    }

    RT_INVALID_CREDENTIALS = {
        'RT4.4 stable (bad credentials)': {
            'url': 'http://localhost:8080/REST/1.0/',
            'default_login': 'idontexist',
            'default_password': 'idonthavepassword',
        },
    }

    RT_MISSING_CREDENTIALS = {
        'RT4.4 stable (missing credentials)': {
            'url': 'http://localhost:8080/REST/1.0/',
        },
    }

    RT_BAD_URL = {
        'RT (bad url)': {
            'url': 'http://httpbin.org/status/404',
            'default_login': 'idontexist',
            'default_password': 'idonthavepassword',
        },
    }

    def _have_creds(*creds_seq):
        return all(creds[name].get('url') for creds in creds_seq for name in creds)

    @staticmethod
    def _fix_unsecure_cookie(tracker: rt.rest1.Rt) -> None:
        """As of RT 5.0.4, cookies returned by the REST API are marked as secure by default.

        This breaks tests though as we are connecting via HTTP. This method fixes these cookies
        for the tests to work.
        """
        cookies = requests.utils.dict_from_cookiejar(tracker.session.cookies)
        tracker.session.cookies.clear()
        tracker.session.cookies.update(cookies)

    @unittest.skipUnless(
        _have_creds(RT_VALID_CREDENTIALS, RT_INVALID_CREDENTIALS, RT_MISSING_CREDENTIALS, RT_BAD_URL),
        'missing credentials required to run test',
    )
    def test_login_and_logout(self):
        for name in self.RT_VALID_CREDENTIALS:
            tracker = rt.rest1.Rt(self.RT_VALID_CREDENTIALS[name]['url'], **self.RT_VALID_CREDENTIALS[name]['support'])
            self.assertTrue(tracker.login(), 'Invalid login to RT demo site ' + name)
            # unsecure cookie
            self._fix_unsecure_cookie(tracker)
            self.assertTrue(tracker.logout(), 'Invalid logout from RT demo site ' + name)
        for name, params in self.RT_INVALID_CREDENTIALS.items():
            tracker = rt.rest1.Rt(**params)
            self.assertFalse(tracker.login(), 'Login to RT demo site ' + name + ' should fail but did not')
            self.assertRaises(rt.exceptions.AuthorizationError, lambda: tracker.search())
        for name, params in self.RT_MISSING_CREDENTIALS.items():
            tracker = rt.rest1.Rt(**params)
            self.assertRaises(rt.exceptions.AuthorizationError, lambda: tracker.login())
        for name, params in self.RT_BAD_URL.items():
            tracker = rt.rest1.Rt(**params)
            self.assertRaises(rt.exceptions.UnexpectedResponseError, lambda: tracker.login())

    @unittest.skipUnless(_have_creds(RT_VALID_CREDENTIALS), 'missing credentials required to run test')
    def test_ticket_operations(self):
        ticket_subject = 'Testing issue ' + ''.join([random.choice(string.ascii_letters) for i in range(15)])
        ticket_text = (
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
        )
        for name in ('RT4.4 stable',):
            url = self.RT_VALID_CREDENTIALS[name]['url']
            default_login = self.RT_VALID_CREDENTIALS[name]['support']['default_login']
            default_password = self.RT_VALID_CREDENTIALS[name]['support']['default_password']
            tracker = rt.rest1.Rt(url, default_login=default_login, default_password=default_password)
            self.assertTrue(tracker.login(), 'Invalid login to RT demo site ' + name)
            # unsecure cookie
            self._fix_unsecure_cookie(tracker)
            # empty search result
            search_result = tracker.search(Subject=ticket_subject)
            self.assertEqual(search_result, [], 'Search for ticket with random subject returned non empty list.')
            # create
            ticket_id = tracker.create_ticket(Subject=ticket_subject, Text=ticket_text)
            self.assertTrue(ticket_id > -1, 'Creating ticket failed.')
            # search
            search_result = tracker.search(Subject=ticket_subject)
            self.assertEqual(len(search_result), 1, 'Created ticket is not found by the subject.')
            self.assertEqual(search_result[0]['id'], 'ticket/' + str(ticket_id), 'Bad id in search result of just created ticket.')
            self.assertEqual(search_result[0]['Status'], 'new', 'Bad status in search result of just created ticket.')
            # search all queues
            search_result = tracker.search(Queue=rt.rest1.ALL_QUEUES, Subject=ticket_subject)
            self.assertEqual(search_result[0]['id'], 'ticket/' + str(ticket_id), 'Bad id in search result of just created ticket.')
            # raw search
            search_result = tracker.search(raw_query=f'Subject="{ticket_subject}"')
            self.assertEqual(len(search_result), 1, 'Created ticket is not found by the subject.')
            self.assertEqual(search_result[0]['id'], 'ticket/' + str(ticket_id), 'Bad id in search result of just created ticket.')
            self.assertEqual(search_result[0]['Status'], 'new', 'Bad status in search result of just created ticket.')
            # raw search all queues
            search_result = tracker.search(Queue=rt.rest1.ALL_QUEUES, raw_query=f'Subject="{ticket_subject}"')
            self.assertEqual(search_result[0]['id'], 'ticket/' + str(ticket_id), 'Bad id in search result of just created ticket.')
            # get ticket
            ticket = tracker.get_ticket(ticket_id)
            self.assertEqual(ticket, search_result[0], 'Ticket get directly by its id is not equal to previous search result.')
            # edit ticket
            requestors = ['tester1@example.com', 'tester2@example.com']
            tracker.edit_ticket(ticket_id, Status='open', Requestors=requestors)
            # get ticket (edited)
            ticket = tracker.get_ticket(ticket_id)
            self.assertEqual(ticket['Status'], 'open', 'Ticket status was not changed to open.')
            self.assertEqual(ticket['Requestors'], requestors, 'Ticket requestors were not added properly.')
            # get history
            hist = tracker.get_history(ticket_id)
            self.assertTrue(len(hist) > 0, 'Empty ticket history.')
            self.assertEqual(hist[0]['Content'], ticket_text, 'Ticket text was not receives is it was submited.')
            # get_short_history
            short_hist = tracker.get_short_history(ticket_id)
            self.assertTrue(len(short_hist) > 0, 'Empty ticket short history.')
            self.assertEqual(short_hist[0][1], 'Ticket created by %s' % default_login)
            # create 2nd ticket
            ticket2_subject = 'Testing issue ' + ''.join([random.choice(string.ascii_letters) for i in range(15)])
            ticket2_id = tracker.create_ticket(Subject=ticket2_subject)
            self.assertTrue(ticket2_id > -1, 'Creating 2nd ticket failed.')
            # edit link
            self.assertTrue(tracker.edit_link(ticket_id, 'DependsOn', ticket2_id))
            # get links
            links1 = tracker.get_links(ticket_id)
            self.assertTrue('DependsOn' in links1, 'Missing just created link DependsOn.')
            self.assertTrue(links1['DependsOn'][0].endswith('ticket/' + str(ticket2_id)), 'Unexpected value of link DependsOn.')
            links2 = tracker.get_links(ticket2_id)
            self.assertTrue('DependedOnBy' in links2, 'Missing just created link DependedOnBy.')
            self.assertTrue(links2['DependedOnBy'][0].endswith('ticket/' + str(ticket_id)), 'Unexpected value of link DependedOnBy.')
            # reply with attachment
            attachment_content = b'Content of attachment.'
            attachment_name = 'attachment-name.txt'
            reply_text = 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.'
            # should provide a content type as RT 4.0 type guessing is broken (missing use statement for guess_media_type in REST.pm)
            self.assertTrue(
                tracker.reply(ticket_id, text=reply_text, files=[(attachment_name, attachment_content, 'text/plain')]),
                'Reply to ticket returned False indicating error.',
            )
            # attachments list
            at_list = tracker.get_attachments(ticket_id)
            self.assertTrue(at_list, 'Empty list with attachment ids, something went wrong.')
            at_names = [at[1] for at in at_list]
            self.assertTrue(attachment_name in at_names, 'Attachment name is not in the list of attachments.')
            # get the attachment and compare it's content
            at_id = at_list[at_names.index(attachment_name)][0]
            at_content = tracker.get_attachment_content(ticket_id, at_id)
            self.assertEqual(at_content, attachment_content, 'Recorded attachment is not equal to the original file.')
            # merge tickets
            self.assertTrue(tracker.merge_ticket(ticket2_id, ticket_id), 'Merging tickets failed.')
            # delete ticket
            self.assertTrue(tracker.edit_ticket(ticket_id, Status='deleted'), 'Ticket delete failed.')
            # get user
            self.assertIn('@', tracker.get_user(default_login)['EmailAddress'])

    @unittest.skipUnless(_have_creds(RT_VALID_CREDENTIALS), 'missing credentials required to run test')
    def test_ticket_operations_admincc_cc(self):
        ticket_subject = 'Testing issue ' + ''.join([random.choice(string.ascii_letters) for i in range(15)])
        ticket_text = (
            'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
        )
        for name in ('RT4.4 stable',):
            url = self.RT_VALID_CREDENTIALS[name]['url']
            default_login = self.RT_VALID_CREDENTIALS[name]['support']['default_login']
            default_password = self.RT_VALID_CREDENTIALS[name]['support']['default_password']
            tracker = rt.rest1.Rt(url, default_login=default_login, default_password=default_password)
            self.assertTrue(tracker.login(), 'Invalid login to RT demo site ' + name)

            # unsecure cookie
            self._fix_unsecure_cookie(tracker)

            ticket_id = tracker.create_ticket(Subject=ticket_subject, Text=ticket_text)
            self.assertTrue(ticket_id > -1, 'Creating ticket failed.')

            # make sure Requestors, AdminCc and Cc are present and an empty list, as would be expected
            ticket = tracker.get_ticket(ticket_id)
            self.assertTrue(len(ticket['Requestors']) >= 0)
            self.assertTrue(len(ticket['AdminCc']) >= 0)
            self.assertTrue(len(ticket['Cc']) >= 0)

            # set requestors
            requestors = ['tester1@example.com', 'tester2@example.com']
            tracker.edit_ticket(ticket_id, Status='open', Requestors=requestors)
            # verify
            ticket = tracker.get_ticket(ticket_id)
            self.assertListEqual(requestors, ticket['Requestors'])

            # set admincc
            admincc = ['tester2@example.com', 'tester3@example.com']
            tracker.edit_ticket(ticket_id, Status='open', AdminCc=admincc)
            # verify
            ticket = tracker.get_ticket(ticket_id)
            self.assertListEqual(requestors, ticket['Requestors'])
            self.assertListEqual(admincc, ticket['AdminCc'])

            # update admincc
            admincc = ['tester2@example.com', 'tester3@example.com', 'tester4@example.com']
            tracker.edit_ticket(ticket_id, Status='open', AdminCc=admincc)
            # verify
            ticket = tracker.get_ticket(ticket_id)
            self.assertListEqual(requestors, ticket['Requestors'])
            self.assertListEqual(admincc, ticket['AdminCc'])

            # unset requestors and admincc
            requestors = []
            admincc = []
            tracker.edit_ticket(ticket_id, Status='open', Requestors=requestors, AdminCc=admincc)
            # verify
            ticket = tracker.get_ticket(ticket_id)
            self.assertListEqual(requestors, ticket['Requestors'])
            self.assertListEqual(admincc, ticket['AdminCc'])


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