#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

from __future__ import division

from kudu.compat import unittest
import kudu


class TestSchema(unittest.TestCase):

    def setUp(self):
        self.columns = [('one', 'int32', False),
                        ('two', 'int8', False),
                        ('three', 'double', True),
                        ('four', 'string', False)]

        self.primary_keys = ['one', 'two']

        self.builder = kudu.schema_builder()
        for name, typename, nullable in self.columns:
            self.builder.add_column(name, typename, nullable=nullable)

        self.builder.set_primary_keys(self.primary_keys)
        self.schema = self.builder.build()

    def test_repr(self):
        result = repr(self.schema)
        for name, _, _ in self.columns:
            assert name in result

        assert 'PRIMARY KEY (one, two)' in result

    def test_schema_length(self):
        assert len(self.schema) == 4

    def test_names(self):
        assert self.schema.names == ['one', 'two', 'three', 'four']

    def test_primary_keys(self):
        assert self.schema.primary_key_indices() == [0, 1]
        assert self.schema.primary_keys() == ['one', 'two']

    def test_getitem_boundschecking(self):
        with self.assertRaises(IndexError):
            self.schema[4]

    def test_getitem_wraparound(self):
        # wraparound
        result = self.schema[-1]
        expected = self.schema[3]

        assert result.equals(expected)

    def test_getitem_string(self):
        result = self.schema['three']
        expected = self.schema[2]

        assert result.equals(expected)

        with self.assertRaises(KeyError):
            self.schema['not_found']

    def test_schema_equals(self):
        assert self.schema.equals(self.schema)

        builder = kudu.schema_builder()
        builder.add_column('key', 'int64', nullable=False, primary_key=True)
        schema = builder.build()

        assert not self.schema.equals(schema)

    def test_column_equals(self):
        assert not self.schema[0].equals(self.schema[1])

    def test_type(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('int32')
         .primary_key()
         .block_size(1048576)
         .nullable(False))
        schema = builder.build()

        tp = schema[0].type
        assert tp.name == 'int32'
        assert tp.type == kudu.schema.INT32

    def test_compression(self):
        builder = kudu.schema_builder()
        builder.add_column('key', 'int64', nullable=False)

        foo = builder.add_column('foo', 'string').compression('lz4')
        assert foo is not None

        bar = builder.add_column('bar', 'string')
        bar.compression(kudu.COMPRESSION_ZLIB)

        with self.assertRaises(ValueError):
            bar = builder.add_column('qux', 'string', compression='unknown')

        builder.set_primary_keys(['key'])
        builder.build()

        # TODO; The C++ client does not give us an API to see the storage
        # attributes of a column

    def test_encoding(self):
        builder = kudu.schema_builder()
        builder.add_column('key', 'int64', nullable=False)

        available_encodings = ['auto', 'plain', 'prefix', 'bitshuffle',
                               'rle', 'dict', kudu.ENCODING_DICT]
        for enc in available_encodings:
            foo = builder.add_column('foo_%s' % enc, 'string').encoding(enc)
            assert foo is not None
            del foo

        bar = builder.add_column('bar', 'string')
        bar.encoding(kudu.ENCODING_PLAIN)

        with self.assertRaises(ValueError):
            builder.add_column('qux', 'string', encoding='unknown')

        builder.set_primary_keys(['key'])
        builder.build()
        # TODO(wesm): The C++ client does not give us an API to see the storage
        # attributes of a column

    def test_decimal(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('decimal')
         .primary_key()
         .nullable(False)
         .precision(9)
         .scale(2))
        schema = builder.build()

        column = schema[0]
        tp = column.type
        assert tp.name == 'decimal'
        assert tp.type == kudu.schema.DECIMAL
        ta = column.type_attributes
        assert ta.precision == 9
        assert ta.scale == 2

    def test_decimal_without_precision(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('decimal')
         .primary_key()
         .nullable(False))

        with self.assertRaises(kudu.KuduInvalidArgument):
            builder.build()

    def test_precision_on_non_decimal_column(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('int32')
         .primary_key()
         .nullable(False)
         .precision(9)
         .scale(2))

        with self.assertRaises(kudu.KuduInvalidArgument):
            builder.build()

    def test_date(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('date')
         .primary_key()
         .nullable(False))
        schema = builder.build()

        column = schema[0]
        tp = column.type
        assert tp.name == 'date'
        assert tp.type == kudu.schema.DATE

    def test_varchar(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('varchar')
         .primary_key()
         .nullable(False)
         .length(10))
        schema = builder.build()

        column = schema[0]
        tp = column.type
        assert tp.name == 'varchar'
        assert tp.type == kudu.schema.VARCHAR
        ta = column.type_attributes
        assert ta.length == 10

    def test_varchar_without_length(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('varchar')
         .primary_key()
         .nullable(False))

        with self.assertRaises(kudu.KuduInvalidArgument):
            builder.build()

    def test_varchar_invalid_length(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('varchar')
         .primary_key()
         .length(0)
         .nullable(False))

        with self.assertRaises(kudu.KuduInvalidArgument):
            builder.build()

    def test_length_on_non_varchar_column(self):
        builder = kudu.schema_builder()
        (builder.add_column('key')
         .type('decimal')
         .primary_key()
         .nullable(False)
         .length(10))

        with self.assertRaises(kudu.KuduInvalidArgument):
            builder.build()

    def test_unsupported_col_spec_methods_for_create_table(self):
        builder = kudu.schema_builder()
        builder.add_column('test', 'int64').rename('test')
        with self.assertRaises(kudu.KuduNotSupported):
            builder.build()

        builder.add_column('test', 'int64').remove_default()
        with self.assertRaises(kudu.KuduNotSupported):
            builder.build()

    def test_set_column_spec_pk(self):
        builder = kudu.schema_builder()
        key = (builder.add_column('key', 'int64', nullable=False)
               .primary_key())
        assert key is not None
        schema = builder.build()
        assert 'key' in schema.primary_keys()

        builder = kudu.schema_builder()
        key = (builder.add_column('key', 'int64', nullable=False,
                                  primary_key=True))
        schema = builder.build()
        assert 'key' in schema.primary_keys()

    def test_partition_schema(self):
        pass

    def test_nullable_not_null(self):
        builder = kudu.schema_builder()
        (builder.add_column('key', 'int64', nullable=False)
         .primary_key())

        builder.add_column('data1', 'double').nullable(True)
        builder.add_column('data2', 'double').nullable(False)
        builder.add_column('data3', 'double', nullable=True)
        builder.add_column('data4', 'double', nullable=False)

        schema = builder.build()

        assert not schema[0].nullable
        assert schema[1].nullable
        assert not schema[2].nullable

        assert schema[3].nullable
        assert not schema[4].nullable

    def test_default_value(self):
        pass

    def test_column_schema_repr(self):
        result = repr(self.schema[0])
        expected = 'ColumnSchema(name=one, type=int32, nullable=False)'
        self.assertEqual(result, expected)
