from unittest import TestCase
from mock import MagicMock

import json

from youtube_transcript_api import YouTubeTranscriptApi, VideoUnavailable
from youtube_transcript_api._cli import YouTubeTranscriptCli


class TestYouTubeTranscriptCli(TestCase):
    def setUp(self):
        self.transcript_mock = MagicMock()
        self.transcript_mock.fetch = MagicMock(return_value=[
            {'text': 'Hey, this is just a test', 'start': 0.0, 'duration': 1.54},
            {'text': 'this is not the original transcript', 'start': 1.54, 'duration': 4.16},
            {'text': 'just something shorter, I made up for testing', 'start': 5.7, 'duration': 3.239}
        ])
        self.transcript_mock.translate = MagicMock(return_value=self.transcript_mock)

        self.transcript_list_mock = MagicMock()
        self.transcript_list_mock.find_generated_transcript = MagicMock(return_value=self.transcript_mock)
        self.transcript_list_mock.find_manually_created_transcript = MagicMock(return_value=self.transcript_mock)
        self.transcript_list_mock.find_transcript = MagicMock(return_value=self.transcript_mock)

        YouTubeTranscriptApi.list_transcripts = MagicMock(return_value=self.transcript_list_mock)

    def test_argument_parsing(self):
        parsed_args = YouTubeTranscriptCli('v1 v2 --format json --languages de en'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.http_proxy, '')
        self.assertEqual(parsed_args.https_proxy, '')

        parsed_args = YouTubeTranscriptCli('v1 v2 --languages de en --format json'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.http_proxy, '')
        self.assertEqual(parsed_args.https_proxy, '')

        parsed_args = YouTubeTranscriptCli(' --format json v1 v2 --languages de en'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.http_proxy, '')
        self.assertEqual(parsed_args.https_proxy, '')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --languages de en --format json '
            '--http-proxy http://user:pass@domain:port '
            '--https-proxy https://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.http_proxy, 'http://user:pass@domain:port')
        self.assertEqual(parsed_args.https_proxy, 'https://user:pass@domain:port')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --languages de en --format json --http-proxy http://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.http_proxy, 'http://user:pass@domain:port')
        self.assertEqual(parsed_args.https_proxy, '')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --languages de en --format json --https-proxy https://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.https_proxy, 'https://user:pass@domain:port')
        self.assertEqual(parsed_args.http_proxy, '')

    def test_argument_parsing__only_video_ids(self):
        parsed_args = YouTubeTranscriptCli('v1 v2'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'pretty')
        self.assertEqual(parsed_args.languages, ['en'])

    def test_argument_parsing__video_ids_starting_with_dash(self):
        parsed_args = YouTubeTranscriptCli('\-v1 \-\-v2 \--v3'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['-v1', '--v2', '--v3'])
        self.assertEqual(parsed_args.format, 'pretty')
        self.assertEqual(parsed_args.languages, ['en'])

    def test_argument_parsing__fail_without_video_ids(self):
        with self.assertRaises(SystemExit):
            YouTubeTranscriptCli('--format json'.split())._parse_args()

    def test_argument_parsing__json(self):
        parsed_args = YouTubeTranscriptCli('v1 v2 --format json'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['en'])

        parsed_args = YouTubeTranscriptCli('--format json v1 v2'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'json')
        self.assertEqual(parsed_args.languages, ['en'])

    def test_argument_parsing__languages(self):
        parsed_args = YouTubeTranscriptCli('v1 v2 --languages de en'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'pretty')
        self.assertEqual(parsed_args.languages, ['de', 'en'])

    def test_argument_parsing__proxies(self):
        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --http-proxy http://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.http_proxy, 'http://user:pass@domain:port')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --https-proxy https://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.https_proxy, 'https://user:pass@domain:port')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2 --http-proxy http://user:pass@domain:port --https-proxy https://user:pass@domain:port'.split()
        )._parse_args()
        self.assertEqual(parsed_args.http_proxy, 'http://user:pass@domain:port')
        self.assertEqual(parsed_args.https_proxy, 'https://user:pass@domain:port')

        parsed_args = YouTubeTranscriptCli(
            'v1 v2'.split()
        )._parse_args()
        self.assertEqual(parsed_args.http_proxy, '')
        self.assertEqual(parsed_args.https_proxy, '')

    def test_argument_parsing__list_transcripts(self):
        parsed_args = YouTubeTranscriptCli('--list-transcripts v1 v2'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertTrue(parsed_args.list_transcripts)

        parsed_args = YouTubeTranscriptCli('v1 v2 --list-transcripts'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertTrue(parsed_args.list_transcripts)

    def test_argument_parsing__translate(self):
        parsed_args = YouTubeTranscriptCli('v1 v2 --languages de en --translate cz'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'pretty')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.translate, 'cz')

        parsed_args = YouTubeTranscriptCli('v1 v2 --translate cz --languages de en'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertEqual(parsed_args.format, 'pretty')
        self.assertEqual(parsed_args.languages, ['de', 'en'])
        self.assertEqual(parsed_args.translate, 'cz')

    def test_argument_parsing__manually_or_generated(self):
        parsed_args = YouTubeTranscriptCli('v1 v2 --exclude-manually-created'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertTrue(parsed_args.exclude_manually_created)
        self.assertFalse(parsed_args.exclude_generated)

        parsed_args = YouTubeTranscriptCli('v1 v2 --exclude-generated'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertFalse(parsed_args.exclude_manually_created)
        self.assertTrue(parsed_args.exclude_generated)

        parsed_args = YouTubeTranscriptCli('v1 v2 --exclude-manually-created --exclude-generated'.split())._parse_args()
        self.assertEqual(parsed_args.video_ids, ['v1', 'v2'])
        self.assertTrue(parsed_args.exclude_manually_created)
        self.assertTrue(parsed_args.exclude_generated)

    def test_run(self):
        YouTubeTranscriptCli('v1 v2 --languages de en'.split()).run()

        YouTubeTranscriptApi.list_transcripts.assert_any_call('v1', proxies=None, cookies=None)
        YouTubeTranscriptApi.list_transcripts.assert_any_call('v2', proxies=None, cookies=None)

        self.transcript_list_mock.find_transcript.assert_any_call(['de', 'en'])

    def test_run__failing_transcripts(self):
        YouTubeTranscriptApi.list_transcripts = MagicMock(side_effect=VideoUnavailable('video_id'))

        output = YouTubeTranscriptCli('v1 --languages de en'.split()).run()

        self.assertEqual(output, str(VideoUnavailable('video_id')))

    def test_run__exclude_generated(self):
        YouTubeTranscriptCli('v1 v2 --languages de en --exclude-generated'.split()).run()

        self.transcript_list_mock.find_manually_created_transcript.assert_any_call(['de', 'en'])

    def test_run__exclude_manually_created(self):
        YouTubeTranscriptCli('v1 v2 --languages de en --exclude-manually-created'.split()).run()

        self.transcript_list_mock.find_generated_transcript.assert_any_call(['de', 'en'])

    def test_run__exclude_manually_created_and_generated(self):
        self.assertEqual(
            YouTubeTranscriptCli(
                'v1 v2 --languages de en --exclude-manually-created --exclude-generated'.split()
            ).run(),
            ''
        )

    def test_run__translate(self):
        YouTubeTranscriptCli('v1 v2 --languages de en --translate cz'.split()).run(),

        self.transcript_mock.translate.assert_any_call('cz')

    def test_run__list_transcripts(self):
        YouTubeTranscriptCli('--list-transcripts v1 v2'.split()).run()

        YouTubeTranscriptApi.list_transcripts.assert_any_call('v1', proxies=None, cookies=None)
        YouTubeTranscriptApi.list_transcripts.assert_any_call('v2', proxies=None, cookies=None)

    def test_run__json_output(self):
        output = YouTubeTranscriptCli('v1 v2 --languages de en --format json'.split()).run()

        # will fail if output is not valid json
        json.loads(output)

    def test_run__proxies(self):
        YouTubeTranscriptCli(
            (
                'v1 v2 --languages de en '
                '--http-proxy http://user:pass@domain:port '
                '--https-proxy https://user:pass@domain:port'
            ).split()
        ).run()

        YouTubeTranscriptApi.list_transcripts.assert_any_call(
            'v1',
            proxies={'http': 'http://user:pass@domain:port', 'https': 'https://user:pass@domain:port'},
            cookies= None
        )

        YouTubeTranscriptApi.list_transcripts.assert_any_call(
            'v2',
            proxies={'http': 'http://user:pass@domain:port', 'https': 'https://user:pass@domain:port'},
            cookies=None
        )

    def test_run__cookies(self):
        YouTubeTranscriptCli(
            (
                'v1 v2 --languages de en '
                '--cookies blahblah.txt'
            ).split()
        ).run()
        YouTubeTranscriptApi.list_transcripts.assert_any_call('v1', proxies=None, cookies='blahblah.txt')
        YouTubeTranscriptApi.list_transcripts.assert_any_call('v2', proxies=None, cookies='blahblah.txt')

