import os
import datetime
import tempfile
import time
import numpy
import io
import traceback
import subprocess
import json
import sys
import pprint


sys.path.insert(0, os.path.dirname(__file__))


class Application:
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start_response = start_response

    def op1(self, data=None):
        if data is None:
            data = traceback.format_exc()

        with io.open('log.txt', 'a') as f:
            f.write(
                '\n%s\n%s\n' % (
                    datetime.datetime.now().isoformat(),
                    data,
                )
            )

    def op2(self, rh):
        t5 = rh.split('_')
        t6 = ['%s%s' % (o[0].upper(), o[1:].lower()) for o in t5]
        return '-'.join(t6)

    def op3(self,):
        for o in [self.input_dat, self.output_dat]:
            if not o is None and os.path.exists(o):
                os.unlink(o)

    def op4(self,):
        self.output_dat = tempfile.mktemp(suffix='.dat')
        self.input_dat = tempfile.mktemp(suffix='.dat')

    def op5(
        self,
        status_code=None,
        status_text=None,
        content_type=None,
        content=None,
        headers_text=None,
    ):
        if not headers_text is None:
            if not any([o.startswith('Content-Length') for o in headers_text]):
                headers_text = \
                    headers_text.replace(
                        'Transfer-Encoding: chunked\r\n',
                        ''
                    )
                headers_text += 'Content-Length: %d\r\n' % len(content)

            t13 = headers_text.splitlines()[0]
            t14 = t13.find(' ')
            t15 = t13[t14 + 1:]
            status_suffix = t15
            headers = [
                (o[:o.find(':')], o[o.find(':') + 1:])
                for o in headers_text.splitlines()[1:]
            ]
        else:
            if status_code is None:
                status_code = 200
            if status_text is None:
                status_text = 'OK'

            if content_type is None:
                content_type = 'text/plain'

            status_suffix = '%d %s' % (status_code, status_text)
            headers = [('Content-Type', content_type)]

        t1 = self.start_response(
            status_suffix,
            headers,
        )
        assert isinstance(content, bytes)

        t1(content)

        return []

    def op6(self, cmd_args,):
        self.op1(json.dumps(cmd_args))

        with subprocess.Popen(
            cmd_args,
            stderr=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stdin=subprocess.PIPE
        ) as p:
            try:
                p.wait(20)
                curl_stderr = p.stderr.read().decode('utf-8')
                response_headers = [
                    o[2:]
                    for o in curl_stderr.splitlines()
                    if o.startswith('< ')
                ]
                t9 = '\r\n'.join(response_headers)
                if not 'Content-Length: 0' in t9:
                    for k in range(100):
                        try:
                            with io.open(self.output_dat, 'rb') as f:
                                t10 = f.read()
                            break
                        except:
                            time.sleep(0.05)
                else:
                    t10 = b''

                try:
                    output_dat_stat = repr(
                        os.stat(self.output_dat)
                    )
                except:
                    output_dat_stat = None

                self.op1(
                    json.dumps(
                        dict(
                            curl_return_code=p.returncode,
                            curl_stderr=curl_stderr,
                            output_dat=self.output_dat,
                            output_dat_stat=output_dat_stat,
                            headers=t9,
                        )
                    )
                )
            finally:
                p.terminate()

        return dict(
            headers_text=t9,
            content=t10
        )

    def op7(self):
        try:
            with io.open(
                os.path.join(
                    os.environ['HOME'],
                    'proxy.json'
                ),
                'r'
            ) as f:
                return numpy.random.choice(json.load(f))
        except:
            with io.open('log.txt', 'a') as f:
                f.write(traceback.format_exc())

            return '127.0.0.1:9050'

    def op8(self, input_content, headers, uri, method):
        try:
            self.op4()

            with io.open(
                self.input_dat,
                'wb'
            ) as f:
                f.write(input_content)


            proxy_url = self.op7()

            t17 = [
                'curl',
                'http://%s%s' % (proxy_url, uri),
                *sum([
                    ['--header', '%s: %s' % (k, v)]
                    for k, v in headers.items()
                ], []),
                '-X', method,
                '-v',
                '--no-buffer',
                '--silent',
                '--data-binary', '@%s' % self.input_dat,
                '--max-filesize', '%d' % (60 * 1024 * 1024),
                '-o', self.output_dat,
                '-q',
            ]

            return self.op6(t17)
        finally:
            self.op3()

    def run(self):
        try:
            t4 = int(self.environ.get('CONTENT_LENGTH', '0'))
            t3 = self.environ['wsgi.input'].read(t4)
            t2 = {
                self.op2(k[5:]) : v
                for k, v in self.environ.items()
                if k.startswith('HTTP_')
            }
            for k, v in self.environ.items():
                if k in [
                    'CONTENT_TYPE',
                ]:
                    t2[self.op2(k)] = v

            t7 = dict(
                uri=self.environ['REQUEST_URI'],
                method=self.environ['REQUEST_METHOD'],
                protocol=self.environ['SERVER_PROTOCOL'],
            )

            o_8 = self.op8(
                t3,
                headers=t2,
                uri=t7['uri'],
                method=t7['method'],
            )

            return self.op5(
                **o_8,
            )
        except:
            self.op1()
            return self.op5(
                content='internal server error',
            )


def application(environ, start_response):
    return Application(
        environ=environ,
        start_response=start_response,
    ).run()