import json
import os
import urllib.request, urllib.parse, urllib.error
from io import StringIO
import csv

from tests.fixtures.base_test import BasePlenarioTest, fixtures_path

# Filters
# =======
# Constants holding query string values, helps to have them all in one place.
# The row counts come from executing equivalents of these queries on posgres.

FLU_BASE = 'flu_shot_clinics__filter='
# Returns 4 rows for this condition.
FLU_FILTER_SIMPLE = '{"op": "eq", "col": "zip", "val": 60620}'
# Returns 10 rows.
FLU_FILTER_SIMPLE2 = '{"op": "eq", "col": "day", "val": "Wednesday"}'
# Returns 1 row.
FLU_FILTER_COMPOUND_AND = FLU_BASE + '{"op": "and", "val": [' + \
                          FLU_FILTER_SIMPLE + ', ' + \
                          FLU_FILTER_SIMPLE2 + ']}'
# Returns 13 rows.
FLU_FILTER_COMPOUND_OR = FLU_BASE + '{"op": "or", "val": [' + \
                          FLU_FILTER_SIMPLE + ', ' + \
                          FLU_FILTER_SIMPLE2 + ']}'
# Returns 4 rows.
FLU_FILTER_NESTED = '{"op": "and", "val": [' \
                    '   {"op": "ge", "col": "date", "val": "2013-11-01"},' \
                    '   {"op": "or", "val": [' + \
                            FLU_FILTER_SIMPLE + ', ' + \
                            FLU_FILTER_SIMPLE2 + \
                    '       ]' \
                    '   }' \
                    ']}'


def get_escaped_geojson(fname):
    pwd = os.path.dirname(os.path.realpath(__file__))
    rect_path = os.path.join(pwd, '../fixtures', fname)
    with open(rect_path, 'r') as rect_json:
        query_rect = rect_json.read()
    escaped_query_rect = urllib.parse.quote(query_rect)
    return escaped_query_rect


def get_loop_rect():
    return get_escaped_geojson('loop_rectangle.json')


class PointAPITests(BasePlenarioTest):

    @classmethod
    def setUpClass(cls):
        super(PointAPITests, cls).setUpClass()
        super(PointAPITests, cls).ingest_points()
        super(PointAPITests, cls).ingest_shapes()

    def get_api_response(self, query_string):
        """This bit of code seems to be repeated alot."""
        query = '/v1/api/' + query_string
        response = self.app.get(query)
        return json.loads(response.data.decode("utf-8"))

    # ========
    # datasets
    # ========

    def test_metadata_no_args(self):
        r = self.get_api_response('datasets')
        self.assertEqual(len(r), 2)
        self.assertEqual(len(r['objects']), 3)

    def test_metadata_big_lower_bound(self):
        r = self.get_api_response('datasets?obs_date__ge=1000-01-01')
        self.assertEqual(len(r), 2)
        self.assertEqual(len(r['objects']), 3)

    def test_metadata_big_upper_bound(self):
        r = self.get_api_response('datasets?obs_date__le=2016-01-01')
        self.assertEqual(len(r), 2)
        self.assertEqual(len(r['objects']), 3)

    def test_metadata_both_bounds(self):
        r = self.get_api_response('datasets?obs_date__le=2016-01-01&obs_date__ge=2000-01-01')
        self.assertEqual(len(r), 2)
        self.assertEqual(len(r['objects']), 3)

    def test_metadata_single_dataset(self):
        r = self.get_api_response('datasets?dataset_name=crimes')
        self.assertEqual(len(r['objects']), 1)
        self.assertEqual(r['objects'][0]['view_url'],
                         "http://data.cityofchicago.org/api/views/ijzp-q8t2/rows")

    def test_metadata_filter(self):
        escaped_query_rect = get_loop_rect()
        query = 'datasets?location_geom__within={}'\
                '&obs_date__ge={}&obs_date__le={}'\
            .format(escaped_query_rect, '2015-1-1', '2016-1-1')
        r = self.get_api_response(query)
        self.assertEqual(len(r['objects']), 1)
        dataset_found = r['objects'][0]
        self.assertEqual(dataset_found['dataset_name'], 'crimes')

    def test_included_fields(self):
        r = self.get_api_response('datasets/?dataset_name=flu_shot_clinics'
                                  '&include_columns=true')
        self.assertEqual(len(r['objects'][0]['columns']), 17)

    ''' /fields '''

    def test_fields(self):
        r = self.get_api_response('fields/flu_shot_clinics')

        # Should be the same length
        # as the number of columns in the source dataset
        self.assertEqual(len(r['objects']), 17)

    # ====================
    # /detail tree filters
    # ====================

    def test_detail_with_simple_flu_filter(self):
        r = self.get_api_response('detail?obs_date__ge=2000&dataset_name=flu_shot_clinics&' + FLU_BASE + FLU_FILTER_SIMPLE)
        self.assertEqual(r['meta']['total'], 4)

    def test_detail_with_compound_flu_filters_and(self):
        r = self.get_api_response('detail?obs_date__ge=2000&dataset_name=flu_shot_clinics&' + FLU_FILTER_COMPOUND_AND)
        self.assertEqual(r['meta']['total'], 1)

    def test_detail_with_compound_flu_filters_or(self):
        r = self.get_api_response('detail?obs_date__ge=2000&dataset_name=flu_shot_clinics&' + FLU_FILTER_COMPOUND_OR)
        self.assertEqual(r['meta']['total'], 13)

    def test_detail_with_nested_flu_filters(self):
        r = self.get_api_response('detail?obs_date__ge=2000&dataset_name=flu_shot_clinics&' + FLU_BASE + FLU_FILTER_NESTED)
        self.assertEqual(r['meta'][
'total'], 4)

    # ============================
    # /detail query string filters
    # ===
=========================

    def test_time_filter(self):
        r = self.get_api_response('detail?dataset_name=flu_shot_clinics'
                                  '&obs_date__ge=2013-09-22'
                                  '&obs_date__le=2013-10-1')
        self.assertEqual(r['meta']['total'], 5)

    def test_detail_with_0_hour_filter(self):
        endpoint = 'detail'
        dataset_arg = '?dataset_name=flu_shot_clinics'
        date_args = '&obs_date__ge=2013-09-22&obs_date__le=2013-10-1'
        hour_arg = '&date__time_of_day_ge=0'

        r = self.get_api_response(endpoint + dataset_arg + date_args + hour_arg)
        self.assertEqual(r['meta']['total'], 5)

    def test_detail_with_both_hour_filters(self):
        endpoint = 'detail'
        dataset_arg = '?dataset_name=crimes'
        date_args = '&obs_date__ge=2000'
        lower_hour_arg = '&date__time_of_day_ge=5'
        upper_hour_arg = '&date__time_of_day_le=17'

        r = self.get_api_response(endpoint + dataset_arg + date_args +
                                  upper_hour_arg + lower_hour_arg)
        self.assertEqual(r['meta']['total'], 3)

    def test_csv_response(self):
        query = '/v1/api/detail/?dataset_name=flu_shot_clinics&obs_date__ge=2013-09-22&obs_date__le=2013-10-1&data_type=csv'
        resp = self.app.get(query)

        mock_csv_file = StringIO(resp.data.decode("utf-8"))
        reader = csv.reader(mock_csv_file)
        lines = [line for line in reader]
        # One header line, 5 data lines
        self.assertEqual(len(lines), 6)
        for line in lines:
            self.assertEqual(len(line), len(lines[0]))

        self.assertTrue('date' in lines[0])
        self.assertTrue('latitude' in lines[0])
        self.assertTrue('longitude' in lines[0])

    def test_geojson_response(self):
        r = self.get_api_response('detail/?dataset_name=flu_shot_clinics'
                                  '&obs_date__ge=2013-09-22'
                                  '&obs_date__le=2013-10-1&data_type=geojson')

        points = r['features']

        self.assertEqual(len(points), 5)
        attributes = points[0]
        self.assertTrue('geometry' in attributes)
        self.assertTrue('latitude' in attributes['properties'])
        self.assertTrue('longitude' in attributes['properties'])

    def test_space_filter(self):
        escaped_query_rect = get_loop_rect()

        r = self.get_api_response('detail/?dataset_name=flu_shot_clinics'
                                  '&obs_date__ge=2013-01-01'
                                  '&obs_date__le=2013-12-31'
                                  '&location_geom__within=' + escaped_query_rect)
        self.assertEqual(r['meta']['total'], 5)

    def test_time_of_day(self):
        r = self.get_api_response('detail/?dataset_name=crimes'
                                  '&o