from __future__ import unicode_literals

import json
import re

from six.moves.urllib.parse import urlsplit

from moto.core.responses import BaseResponse
from .models import polly_backends
from .resources import LANGUAGE_CODES, VOICE_IDS

LEXICON_NAME_REGEX = re.compile(r'^[0-9A-Za-z]{1,20}$')


class PollyResponse(BaseResponse):
    @property
    def polly_backend(self):
        return polly_backends[self.region]

    @property
    def json(self):
        if not hasattr(self, '_json'):
            self._json = json.loads(self.body)
        return self._json

    def _error(self, code, message):
        return json.dumps({'__type': code, 'message': message}), dict(status=400)

    def _get_action(self):
        # Amazon is now naming things /v1/api_name
        url_parts = urlsplit(self.uri).path.lstrip('/').split('/')
        # [0] = 'v1'

        return url_parts[1]

    # DescribeVoices
    def voices(self):
        language_code = self._get_param('LanguageCode')
        next_token = self._get_param('NextToken')

        if language_code is not None and language_code not in LANGUAGE_CODES:
            msg = "1 validation error detected: Value '{0}' at 'languageCode' failed to satisfy constraint: " \
                  "Member must satisfy enum value set: [{1}]".format(language_code, ', '.join(LANGUAGE_CODES))
            return msg, dict(status=400)

        voices = self.polly_backend.describe_voices(language_code, next_token)

        return json.dumps({'Voices': voices})

    def lexicons(self):
        # Dish out requests based on methods

        # anything after the /v1/lexicons/
        args = urlsplit(self.uri).path.lstrip('/').split('/')[2:]

        if self.method == 'GET':
            if len(args) == 0:
                return self._get_lexicons_list()
            else:
                return self._get_lexicon(*args)
        elif self.method == 'PUT':
            return self._put_lexicons(*args)
        elif self.method == 'DELETE':
            return self._delete_lexicon(*args)

        return self._error('InvalidAction', 'Bad route')

    # PutLexicon
    def _put_lexicons(self, lexicon_name):
        if LEXICON_NAME_REGEX.match(lexicon_name) is None:
            return self._error('InvalidParameterValue', 'Lexicon name must match [0-9A-Za-z]{1,20}')

        if 'Content' not in self.json:
            return self._error('MissingParameter', 'Content is missing from the body')

        self.polly_backend.put_lexicon(lexicon_name, self.json['Content'])

        return ''

    # ListLexicons
    def _get_lexicons_list(self):
        next_token = self._get_param('NextToken')

        result = {
            'Lexicons': self.polly_backend.list_lexicons(next_token)
        }

        return json.dumps(result)

    # GetLexicon
    def _get_lexicon(self, lexicon_name):
        try:
            lexicon = self.polly_backend.get_lexicon(lexicon_name)
        except KeyError:
            return self._error('LexiconNotFoundException', 'Lexicon not found')

        result = {
            'Lexicon': {
                'Name': lexicon_name,
                'Content': lexicon.content
            },
            'LexiconAttributes': lexicon.to_dict()['Attributes']
        }

        return json.dumps(result)

    # DeleteLexicon
    def _delete_lexicon(self, lexicon_name):
        try:
            self.polly_backend.delete_lexicon(lexicon_name)
        except KeyError:
            return self._error('LexiconNotFoundException', 'Lexicon not found')

        return ''

    # SynthesizeSpeech
    def speech(self):
        # Sanity check params
        args = {
            'lexicon_names': None,
            'sample_rate': 22050,
            'speech_marks': None,
            'text': None,
            'text_type': 'text'
        }

        if 'LexiconNames' in self.json:
            for lex in self.json['LexiconNames']:
                try:
                    self.polly_backend.get_lexicon(lex)
                except KeyError:
                    return self._error('LexiconNotFoundException', 'Lexicon not found')

            args['lexicon_names'] = self.json['LexiconNames']

        if 'OutputFormat' not in self.json:
            return self._error('MissingParameter', 'Missing parameter OutputFormat')
        if self.json['OutputFormat'] not in ('json', 'mp3', 'ogg_vorbis', 'pcm'):
            return self._error('InvalidParameterValue', 'Not one of json, mp3, ogg_vorbis, pcm')
        args['output_format'] = self.json['OutputFormat']

        if 'SampleRate' in self.json:
            sample_rate = int(self.json['SampleRate'])
            if sample_rate not in (8000, 16000, 22050):
                return self._error('InvalidSampleRateException', 'The specified sample rate is not valid.')
            args['sample_rate'] = sample_rate

        if 'SpeechMarkTypes' in self.json:
            for value in self.json['SpeechMarkTypes']:
                if value not in ('sentance', 'ssml', 'viseme', 'word'):
                    return self._error('InvalidParameterValue', 'Not one of sentance, ssml, viseme, word')
            args['speech_marks'] = self.json['SpeechMarkTypes']

        if 'Text' not in self.json:
            return self._error('MissingParameter', 'Missing parameter Text')
        args['text'] = self.json['Text']

        if 'TextType' in self.json:
            if self.json['TextType'] not in ('ssml', 'text'):
                return self._error('InvalidParameterValue', 'Not one of ssml, text')
            args['text_type'] = self.json['TextType']

        if 'VoiceId' not in self.json:
            return self._error('MissingParameter', 'Missing parameter VoiceId')
        if self.json['VoiceId'] not in VOICE_IDS:
            return self._error('InvalidParameterValue', 'Not one of {0}'.format(', '.join(VOICE_IDS)))
        args['voice_id'] = self.json['VoiceId']

        # More validation
        if len(args['text']) > 3000:
            return self._error('TextLengthExceededException', 'Text too long')

        if args['speech_marks'] is not None and args['output_format'] != 'json':
            return self._error('MarksNotSupportedForFormatException', 'OutputFormat must be json')
        if args['speech_marks'] is not None and args['text_type'] == 'text':
            return self._error('SsmlMarksNotSupportedForTextTypeException', 'TextType must be ssml')

        content_type = 'audio/json'
        if args['output_format'] == 'mp3':
            content_type = 'audio/mpeg'
        elif args['output_format'] == 'ogg_vorbis':
            content_type = 'audio/ogg'
        elif args['output_format'] == 'pcm':
            content_type = 'audio/pcm'

        headers = {'Content-Type': content_type}

        return '\x00\x00\x00\x00\x00\x00\x00\x00', headers
