import re

from six import string_types

from ksql.error import SQLTypeNotImplementYetError, \
					   IllegalTableTypeError, \
					   IllegalValueFormatError, \
					   SQLFormatNotImplementError, \
					   BuildNotImplmentError

class SQLBuilder(object):

	@classmethod
	def build(self, sql_type, **kwargs):
		if sql_type == 'create':
			sql_builder = CreateBuilder(kwargs.pop('table_type'))
			sql_str = sql_builder.build(kwargs.pop('table_name'), 
							  kwargs.pop('columns_type'),
							  kwargs.pop('topic'),
							  kwargs.pop('value_format'),
							  kwargs.pop('key', None))

		elif sql_type == 'create_as':
			sql_builder = CreateAsBuilder(kwargs.pop('table_type'))
			sql_str = sql_builder.build(table_name = kwargs.pop('table_name'), 
							  			select_columns = kwargs.pop('select_columns', None),
							  			src_table = kwargs.pop('src_table'),
							  			kafka_topic = kwargs.pop('kafka_topic'),
							  			value_format = kwargs.pop('value_format'),
							  			partition_by = kwargs.pop('partition_by', None),
							  			**kwargs)

		else:
			raise SQLTypeNotImplementYetError(sql_type)

		return sql_str


class BaseCreateBuilder(object):
	def __init__(self, table_type, sql_format=None):
		self.table_types = ['table', 'stream']
		self.value_formats = ['delimited', 'json', 'avro']
		self.table_type = table_type
		self.sql_format = sql_format

		if table_type.lower() not in self.table_types:
			raise IllegalTableTypeError(table_type)

		if not sql_format:
			raise SQLFormatNotImplementError()

	def build(self):
		raise BuildNotImplmentError()

	def _parsed_with_properties(self, **kwargs):
		properties = ['']
		for prop in self.properties:
			if prop in kwargs.keys():
				value = kwargs.get(prop)
				if isinstance(value, string_types):
					p = "{}='{}'".format(prop, kwargs.get(prop))
				else:
					p = "{}={}".format(prop, kwargs.get(prop))
				
				properties.append(p)

		return ', '.join(properties)


class CreateBuilder(BaseCreateBuilder):
	def __init__(self, table_type):
		str_format = "CREATE {} {} ({}) WITH (kafka_topic='{}', value_format='{}'{});"
		super(CreateBuilder, self).__init__(table_type, str_format)
		self.properties = ['kafka_topic', 'value_format', 'key', 'timestamp']


	def build(self, table_name, columns_type=[], topic=None, value_format='DELIMITED', key=None):
		if value_format.lower() not in self.value_formats:
			raise IllegalValueFormatError(value_format)
			
		built_colums_type = self._build_colums_type(columns_type)
		built_key = self._build_key(key)

		sql_str = self.sql_format.format(self.table_type, table_name, built_colums_type, topic, value_format, built_key)
		return sql_str

	@staticmethod
	def _build_colums_type(columns_type):
		built_columns_type = ', '.join(columns_type)
		return built_columns_type

	@staticmethod
	def _build_key(key):
		if key:
			built_key = ", key='{}'".format(key)
			return built_key
		else:
			return ''


class CreateAsBuilder(BaseCreateBuilder):
	def __init__(self, table_type):
		str_format = "CREATE {} {} WITH (kafka_topic='{}', value_format='{}'{}) AS SELECT {} FROM {} {} {}"
		super(CreateAsBuilder, self).__init__(table_type, str_format)
		self.properties = ['kafka_topic', 'value_format', 'partitions', 'replicas', 'timestamp']

		
	def build(self, table_name, select_columns, src_table, kafka_topic=None, 
			  value_format='DELIMITED', conditions=[], partition_by=None, **kwargs):

		if value_format.lower() not in self.value_formats:
			raise IllegalValueFormatError(value_format)
		
		if not kafka_topic:
			kafka_topic = table_name

		select_clause, where_clause, partition_by_clause, properties = \
			self._build_clauses(select_columns, conditions, partition_by, **kwargs)

		sql_str = self.sql_format.format(self.table_type, table_name, 
										kafka_topic, value_format, properties, 
										select_clause, src_table, where_clause, 
										partition_by_clause)

		cleaned_sql_str = re.sub( '\s+', ' ', sql_str).strip()

		return cleaned_sql_str

	def _build_clauses(self, select_columns, conditions, partition_by, **kwargs):
		select_clause = self._build_select_clause(select_columns)
		where_clause = self._build_where_clause(conditions)
		partition_by_clause = self._build_partition_by_clause(partition_by)
		properties = self._parsed_with_properties(**kwargs)

		return select_clause, where_clause, partition_by_clause, properties

	@staticmethod
	def _build_where_clause(conditions):
		if len(conditions) > 0:
			where_clause = "where {}".format(conditions).replace('"','\'')
			return where_clause
		else:
			return ''

	@staticmethod
	def _build_select_clause(select_columns):
		select_clause = '*'
		if select_columns:
			if len(select_columns) > 0:
				select_clause = ', '.join(select_columns)

		return select_clause

	@staticmethod
	def _build_partition_by_clause(partition_by):
		if partition_by:
			return 'PARTITION BY {}'.format(partition_by)
		else:
			return ''
