from typing import List, Dict
from ultipa.printer.prettyPrint import PrettyPrint, PrettyTable
from ultipa.structs.GraphSet import GraphSet
from ultipa.structs.Property import Property
from ultipa.structs.Schema import Schema
from ultipa.structs.DBType import DBType
from ultipa.structs.User import User
from ultipa.structs.Policy import Policy
from ultipa.structs.Privilege import Privilege
from ultipa.structs.Index import Index
from ultipa.structs.Stats import Stats
from ultipa.structs.Top import Top
from ultipa.structs.Algo import Algo
from ultipa.types.types import BaseModel, Node, Edge, DataItem, Status, \
	UltipaStatistics, ReturnReq, BaseUqlReply, ResultType, ResultAlias, ExplainPlan
from ultipa.utils.convert import Any
from typing import Callable



class PropertyTable(BaseModel):
	name: str
	data: List[Property]

	def __init__(self, name, data):
		self.name = name
		self.data = data



class Response(BaseModel):
	def __init__(self, status: Status = None, data: BaseUqlReply = None,
				 req: ReturnReq = None, statistics: UltipaStatistics = None, aliases: List[ResultAlias] = None):
		self.status = status
		self.data = data
		self.statistics = statistics
		self.req = req
		self.aliases = aliases

	def Print(self):
		pretty = PrettyPrint()
		pretty.prettyStatus(self.status)
		if self.status.code != 0:
			return
		if self.statistics:
			pretty.prettyStatistics(self.statistics)

		if isinstance(self.data, list):
			dict_list = []
			for i in self.data:
				if isinstance(i,PropertyTable):
					dict_list.append(i.toDict())
				else:
					dict_list.append(i.__dict__)
			pretty.prettyDataList(dict_list)

		if isinstance(self.data, Any):
			dict_list = self.data.__dict__
			pretty.prettyData(dict_list)


class UltipaResponse(BaseModel):
	def __init__(self, status: Status = None, items: Dict = None,
				 req: ReturnReq = None, statistics: UltipaStatistics = None, aliases: List[ResultAlias] = None,
				 explainPlan: List[ExplainPlan] = None):
		self.status = status
		self.items = items
		self.aliases = aliases
		self.req = req
		self.statistics = statistics
		self.explainPlan = explainPlan

	def alias(self, alias: str) -> DataItem:
		if self.items == None:
			return DataItem(alias, None, ResultType.getTypeStr(ResultType.RESULT_TYPE_UNSET))
		if self.items.get(alias):
			return self.items.get(alias)
		return DataItem(alias, None, ResultType.getTypeStr(ResultType.RESULT_TYPE_UNSET))

	def get(self, index: int) -> DataItem:
		if len(self.aliases) - 1 >= index:
			data = self.items.get(self.aliases[index].alias)
			if data:
				return data
			if self.aliases[index].result_type == ResultType.getTypeStr(ResultType.RESULT_TYPE_UNSET):
				return DataItem(None, None, ResultType.getTypeStr(ResultType.RESULT_TYPE_UNSET))
			else:
				return data
		return DataItem(None, None, ResultType.getTypeStr(ResultType.RESULT_TYPE_UNSET))

	def getExplainPlan(self):
		return self.explainPlan

	def Print(self):
		pretty = PrettyPrint()
		pretty.prettyStatus(self.status)
		if self.status.code != 0:
			return
		pretty.prettyStatistics(self.statistics)
		explains = []
		for key in self.items:
			dataItem = self.items.get(key)
			if dataItem.type == ResultType.getTypeStr(ResultType.RESULT_TYPE_NODE):
				pretty.prettyNode(dataItem)

			elif dataItem.type == ResultType.getTypeStr(ResultType.RESULT_TYPE_EDGE):
				pretty.prettyEdge(dataItem)

			elif dataItem.type == ResultType.getTypeStr(ResultType.RESULT_TYPE_TABLE):
				pretty.prettyTable(dataItem)

			elif dataItem.type == ResultType.getTypeStr(ResultType.RESULT_TYPE_PATH):
				pretty.prettyPath(dataItem)

			elif dataItem.type == ResultType.getTypeStr(ResultType.RESULT_TYPE_ATTR):
				pretty.prettyAttr(dataItem)


		if self.explainPlan:
			for explan in self.explainPlan:
				explains.append(explan)
			if explains:
				pretty.prettyTree(explains)

class UQLResponseStream:
    def __init__(self):
        self._listeners = {}
        self._default_handlers = {
            "start": self._default_start_handler,
            "end": self._default_end_handler
        }

    def on(self, event: str, handler: Callable):
        if event not in self._listeners:
            self._listeners[event] = []
        self._listeners[event].append(handler)

    def emit(self, event: str, *args, **kwargs):
        if event in self._listeners:
            for handler in self._listeners[event]:
                handler(*args, **kwargs)
        elif event in self._default_handlers:
            self._default_handlers[event](*args, **kwargs)

    def _default_start_handler(self, request_config):
        print("Stream started with request:", request_config)

    def _default_end_handler(self, request_config):
        print("Stream ended with request:", request_config)


class ResponseCommon(Response):
	data: None



# class User(BaseModel):
# 	username: str
# 	create: str
# 	last_login_time: str
# 	graphPrivileges: dict
# 	systemPrivileges: List[str]
# 	policies: List[str]


# class Index(BaseModel):
# 	name: str
# 	properties: str
# 	schema: str
# 	status: str
# 	size:str
# 	dbType:DBType


# class IndexTable:
# 	name: str
# 	data: List[Index]


# class Stat(BaseModel):
# 	cpuUsage: str
# 	memUsage: str
# 	company: str
# 	cpuCores: str
# 	expiredDate: str
# 	serverType: str
# 	version: str




# class Privilege(BaseModel):
# 	graphPrivileges: List[str]
# 	systemPrivileges: List[str]


# class Policy(BaseModel):
# 	name: str
# 	policies: List[str]
# 	graphPrivileges: dict
# 	systemPrivileges: List[str]


# class Top(BaseModel):
# 	process_id: str
# 	process_uql: str
# 	duration: str


class Return_Type(BaseModel):
	is_realtime: bool
	is_visualization: bool
	is_wirte_back: bool


class Task_info(BaseModel):
	task_id: int
	algo_name: str
	start_time: int
	writing_start_time: int
	end_time: int
	time_cost: int
	TASK_STATUS: int
	status_code: str
	engine_cost: int
	return_type: Return_Type


class Task(BaseModel):
	param: dict
	task_info: Task_info
	result: dict


class SearchKhop(BaseModel):
	values: dict
	nodes: List[Node]


class Path(BaseModel):
	nodes: List[Node]
	edges: List[Edge]


class SearchPath(BaseModel):
	paths: List[Path]


class NodeSpread(SearchPath):
	pass


class AutoNet(SearchPath):
	pass


class AlgoResultOpt(BaseModel):
	can_realtime: bool
	can_visualization: bool
	can_write_back: bool

class Exta(BaseModel):
	author:str
	detail:str
	name:str
	version:str

# class Algo(BaseModel):
# 	param: dict
# 	name: str
# 	result_opt: AlgoResultOpt

class ResponseGraph(Response):
	data: GraphSet

class ResponseListGraph(Response):
	data: List[GraphSet]
	

class ResponeListExta(Response):
	data: List[Exta]	

class ResponseSchema(Response):
	items:Schema

class ResponseListSchema(UltipaResponse):
	items:List[Schema]	


class ResponseListIndex(UltipaResponse):
	data: List[Index]


# class ResponseListFulltextIndex(ResponseListIndex):
# 	pass


class ResponseSearchEdge(UltipaResponse):
	items: List[DataItem]


class ResponseSearchNode(UltipaResponse):
	items: List[DataItem]


class ResponseBulk:
	uuids: List[int]
	errorItem: Dict


class ResponseInsertNode(Response):
	data: List[Node]

class ResponseInsertEdge(Response):
	data: List[Edge]	

class ResponseDeleteNode(Response):
	data: List[Node]

class ResponseDeleteEdge(Response):
	data: List[Edge]		



class ResponseBatchAutoInsert(Response):
	data: Dict[str, ResponseBulk]


class InsertResponse(Response):
	data: ResponseBulk


class ResponseListPolicy(Response):
	data: List[Policy]


class ResponsePolicy(Response):
	data: Policy


class ResponsePrivilege(Response):
	data: List[Privilege]


class ResponseListProperty(UltipaResponse):
	items: List[Property]


class ResponseProperty(Response):
	data: List[Property]


class ResponseListTop(Response):
	data: List[Top]


class ResponseListTask(Response):
	data: List[Task]


class ResponseUser(Response):
	data: User


class ResponseListUser(Response):
	data: List[User]


class ResponseListAlgo(Response):
	data: List[Algo]


class Cluster:
	host: str
	status: bool
	cpuUsage: str
	memUsage: str
	isLeader: bool
	isFollowerReadable: bool
	isAlgoExecutable: bool
	isUnset: bool


class ClusterInfo(Response):
	data: List[Cluster]


class ResponseStat(Response):
	data: Stats


class ResponseExport(Response):
	data: List[Node]


