import gzip
import json
import os
import random
import re
import time
import urllib
import yaml

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer


PORT = 9080
SERVICES_CONFIG = os.getenv('SRV_CONFIGS_PATH') + '/backend.main.yaml'


class EchoServer(BaseHTTPRequestHandler):
    """Simple server that should return a different response every time
    an endpoint is hit, including echoing back request information.

    For GET requests, return 'GET' followed by the timestamp and any incoming
        request headers.
    For POST requests, return 'POST', followed by the data, followed by
        the timestamp, followed by any incoming request headers.
    For HEAD requests, just send back a 200.
    """

    def _write_response(self, method, body=None, gzipped=False):
        self.send_header('Content-Type', 'application/json')
        self.send_header('Connection', 'close')
        self.end_headers()
        response = {
            'method': method,
            'timestamp': time.time(),
            'received_headers': self.headers.dict,
            'body': body,
            'null_value': None,
        }

        if gzipped:
            f = gzip.GzipFile(fileobj=self.wfile)
            f.write(json.dumps(response))
        else:
            self.wfile.write(json.dumps(response))

    def handle_bulk(self, cache_name, delimeter):
        """
        This function is targeted to test the behavior of receiving
        bulk endpoints. Here we introduce the feature of treating each id
        as a separate request. There are also valid ids (1-999) and invalid
        ids.
        We also handle special headers such as gzip encoding and returning text.
        We return text to verify that spectre doesn't crash when it doens't
        receive json and defaults to normal forwarding behavior.
        """
        yaml_file = open(SERVICES_CONFIG)
        cached_endpoints = yaml.load(yaml_file)['cached_endpoints']
        cache_name_configs = cached_endpoints[cache_name]
        if not cache_name_configs['bulk_support']:
            self.send_response(500)
            return 'ERROR in config file'
        self.send_response(200)

        if('test-content-type' in self.headers and self.headers['test-content-type'] == 'text'):
            self.send_header('Content-Type', 'text')
            self.end_headers()
            return 'this is text'

        if ('test-content-type' in self.headers and
                self.headers['test-content-type'] == 'application/json; charset=utf-8'
        ):
            self.send_header('Content-Type', 'application/json; charset=utf-8')
        else:
            self.send_header('Content-Type', 'application/json')
        self.end_headers()
        try:
            pattern = cache_name_configs['pattern']
            ids = re.match(pattern, self.path).group(2)
            ids = ids.split(delimeter)
            result = []
            for i in ids:
                try:
                    i = int(i)
                    if i > 0 and i < 1000:
                        result.append({'bulk_id': i, 'empty_array': [], 'null_value': None})
                except ValueError:
                    # If the ID is a string, then convert it back to unicode to emulate
                    # what would happen in a real webapp. This is, incidentally, what
                    # we want to test with TestGetBulkRequest::test_unicode_chars_in_bulk_response
                    result.append({'bulk_id': urllib.unquote(i), 'empty_array': [], 'null_value': None})
            if ('accept-encoding' in self.headers and self.headers['accept-encoding'] == 'gzip'):
                return 'hahaha giberish'
        except:
            # if there are any failures, just return an empty array
            result = []
        random.shuffle(result)
        return json.dumps(result)

    def do_GET(self):
        should_sleep = re.search('sleep=(\d+)', self.path)
        if should_sleep:
            delay_ms = int(should_sleep.group(1))
            time.sleep(delay_ms / 1000.0)

        should_error = re.search('error_status=([\d]+)', self.path)
        if should_error:
            self.send_response(int(should_error.group(1)))
            self.end_headers()
            return

        should_drop_connection = re.search('drop_connection=true', self.path)
        if should_drop_connection:
            # Don't send headers or anything, just close the connection
            # Similar to what happens on 502 errors
            return

        if self.path.startswith('/not_authorized'):
            self.send_response(403)
            self.end_headers()
            self.wfile.write('<html><boyd>403 Forbidden</body></html>')
            return
        elif self.path.endswith('/status'):
            self.send_response(200)
            self.end_headers()
            self.wfile.write('Backend is alive\n')
            return
        elif self.path.startswith('/bulk_requester_2'):
            result = self.handle_bulk('bulk_requester_default', ',')
            self.wfile.write(result)
            return
        elif self.path.startswith('/bulk_requester'):
            result = self.handle_bulk('bulk_requester_does_not_cache_missing_ids', '%2C')
            self.wfile.write(result)
            return
        else:
            self.send_response(200)

        # Send some response headers
        self.send_header('Some-Header', 'abc')
        self.send_header('Uncacheable-Header', 'def')

        self._write_response('GET', gzipped=self.path.endswith('/gzipped'))

    def do_POST(self):
        self.send_response(200)
        length = int(self.headers.getheader('content-length'))
        body = self.rfile.read(length)
        self._write_response('POST', body=body)

    def do_HEAD(self):
        self.send_response(200)
        self.end_headers()


def main():
    server = HTTPServer(('', PORT), EchoServer)
    server.serve_forever()


if __name__ == '__main__':
    main()
