from contextlib import contextmanager
import gzip
import json
import os
import pytest
import pickle
import sys
import tempfile
import vcr

from gcsfs.core import GCSFileSystem
from gcsfs.tests.settings import (TEST_BUCKET, TEST_PROJECT, RECORD_MODE,
                                  GOOGLE_TOKEN)


def before_record_response(response):
    r = pickle.loads(pickle.dumps(response))
    try:
        try:
            data = json.loads(gzip.decompress(r['body']['string']).decode())
            if 'access_token' in data:
                data['access_token'] = 'xxx'
            if 'id_token' in data:
                data['id_token'] = 'xxx'
            r['body']['string'] = gzip.compress(
                    json.dumps(data).replace(
                            TEST_PROJECT, 'test_project').encode())
        except (OSError, TypeError, ValueError):
            r['body']['string'] = r['body']['string'].replace(
                    TEST_PROJECT.encode(), b'test_project')
    except Exception:
        pass
    return r


def before_record(request):
    r = pickle.loads(pickle.dumps(request))
    r.uri = request.uri.replace(TEST_PROJECT, 'test_project')
    return r


def matcher(r1, r2):
    if r1.uri.replace(TEST_PROJECT,
                      'test_project') != r2.uri.replace(TEST_PROJECT,
                      'test_project'):
        print('uri mismatch')
        return False
    if r1.body != r2.body:
        print('body mismatch')
        return False
    if r1.method != r2.method:
        print('method mismatch')
        return False
    for key in ['Content-Length', 'Content-Type', 'Range', 'Server']:
        if r1.head.get(key, '') != r2.head.get(key, ''):
            print('header mismatch', key)
            return False
    print('match')
    return True

recording_path = os.path.join(os.path.dirname(__file__), 'recordings')

my_vcr = vcr.VCR(
    cassette_library_dir=recording_path,
    record_mode=RECORD_MODE,
    path_transformer=vcr.VCR.ensure_suffix('.yaml'),
    filter_headers=['Authorization'],
    filter_query_parameters=['refresh_token', 'upload_id', 'client_id',
                             'client_secret'],
    before_record_response=before_record_response,
    before_record=before_record
    )
my_vcr.register_matcher('all', matcher)
files = {'test/accounts.1.json':  (b'{"amount": 100, "name": "Alice"}\n'
                                   b'{"amount": 200, "name": "Bob"}\n'
                                   b'{"amount": 300, "name": "Charlie"}\n'
                                   b'{"amount": 400, "name": "Dennis"}\n'),
         'test/accounts.2.json':  (b'{"amount": 500, "name": "Alice"}\n'
                                   b'{"amount": 600, "name": "Bob"}\n'
                                   b'{"amount": 700, "name": "Charlie"}\n'
                                   b'{"amount": 800, "name": "Dennis"}\n')}

csv_files = {'2014-01-01.csv': (b'name,amount,id\n'
                                b'Alice,100,1\n'
                                b'Bob,200,2\n'
                                b'Charlie,300,3\n'),
             '2014-01-02.csv': (b'name,amount,id\n'),
             '2014-01-03.csv': (b'name,amount,id\n'
                                b'Dennis,400,4\n'
                                b'Edith,500,5\n'
                                b'Frank,600,6\n')}
text_files = {'nested/file1': b'hello\n',
              'nested/file2': b'world',
              'nested/nested2/file1': b'hello\n',
              'nested/nested2/file2': b'world'}
a = TEST_BUCKET+'/tmp/test/a'
b = TEST_BUCKET+'/tmp/test/b'
c = TEST_BUCKET+'/tmp/test/c'
d = TEST_BUCKET+'/tmp/test/d'


@contextmanager
def ignoring(*exceptions):
    try:
        yield
    except exceptions:
        pass


@pytest.yield_fixture()
def tempdir():
    d = tempfile.mkdtemp()
    yield d
    if os.path.exists(d):
        shutil.rmtree(d, ignore_errors=True)


@contextmanager
def tmpfile(extension='', dir=None):
    extension = '.' + extension.lstrip('.')
    handle, filename = tempfile.mkstemp(extension, dir=dir)
    os.close(handle)
    os.remove(filename)

    try:
        yield filename
    finally:
        if os.path.exists(filename):
            if os.path.isdir(filename):
                shutil.rmtree(filename)
            else:
                with ignoring(OSError):
                    os.remove(filename)


@pytest.yield_fixture
def token_restore():
    try:
        cache = GCSFileSystem.tokens
        GCSFileSystem.tokens = {}
        yield
    finally:
        GCSFileSystem.tokens = cache
        GCSFileSystem._save_tokens()


@contextmanager
def gcs_maker(populate=False):
    gcs = GCSFileSystem(TEST_PROJECT, token=GOOGLE_TOKEN)
    try:
        if not gcs.exists(TEST_BUCKET):
            gcs.mkdir(TEST_BUCKET)
        for k in [a, b, c, d]:
            try:
                gcs.rm(k)
            except:
                pass
        if populate:
            for flist in [files, csv_files, text_files]:
                for fname, data in flist.items():
                    with gcs.open(TEST_BUCKET+'/'+fname, 'wb') as f:
                        f.write(data)
        yield gcs
    finally:
        [gcs.rm(f) for f in gcs.walk(TEST_BUCKET)]
        # gcs.rmdir(settings.TEST_BUCKET)
