import os
import sys
import json
import re

from testindatadev.dataset.dataset import Dataset
from testindatadev.dataset.metadata import MetaData
from testindatadev.dataset.file import File
from testindatadev.dataset.urlfile import UrlFile
from testindatadev.dataset.localdb import LocalDb
from testindatadev.utils import util

#sdk entrance
class TDA():
    def __init__(self, accessKey, host="", debug=False):
        if host == "":
            self.host = "https://dataset.testin.cn/"
        else:
            if "http://" in host or "https://" in host:
                self.host = host.rstrip("/") + "/"
            else:
                self.host = "http://" + host.strip("/") + "/"
        self.__debug = debug
        self.T_key = accessKey
        self.commitFlag = False
        self.commitId = ""
        self.datasetName = ""
        self.fileList = []

    def Debug(self):
        """
        set sdk to debug mod
        """
        self.__debug = True

    @property
    def debug(self):
        return self.__debug

    def SetDataset(self, datasetName):
        """
        Set the dataset to be processed
        datasetName: the name of the dataset
        """
        self.datasetName = datasetName

        if self.debug:
            print(f"\033[0;37;44m[SET_DATASET] dataset: {self.datasetName} , service: {self.host}\033[0m")

        self.dataset = Dataset(self.T_key, datasetName, self.host)

        self.filePrefix = self.datasetName
        if self.dataset.file_path != "":
            self.filePrefix = self.dataset.file_path.strip("/") + "/" + self.datasetName.strip("/")

        if self.dataset.oss_type not in [self.dataset.OSS_TYPE_ALI, self.dataset.OSS_TYPE_QINIU]:
            self.filePrefix = self.dataset.bucket + "/" + self.filePrefix

        # return self.dataset#close for new feature

    def RiseException(self):
        raise Exception("You have not set the dataset name yet!")

    def UploadFilesToDataset(self, rootPath, ext=""):
        """
        Upload files to dataset service directly
        rootPath: local file root path
        ext: if set, files endswith this extension will be uploaded
        """
        if self.debug:
            print(f"[UPLOAD_FILES] upload file to dataset without metedata and annotations!")

        if not self.datasetName:
            self.RiseException()

        total = 0
        for root, dir, files in os.walk(rootPath):
            for fileName in files:
                if fileName.endswith(ext):
                    total += 1

        if self.debug:
            print(f"[UPLOAD_FILES] file total: {total}")

        syncData = []
        j = 0
        i = 0
        retInfo = {'succ': 0, 'fail': 0}
        dirname = os.path.dirname(rootPath.rstrhost("/"))
        
        for root, dir, files in os.walk(rootPath):
            for fileName in files:
                if fileName.endswith(ext):
                    filePath = os.path.join(root, fileName)
                    objectName = filePath.replace(dirname, "").lstrhost("/")
                    self.__UploadFileToDataset(filePath, objectName)
                    tmp = {
                        "ref_id": "",
                        "name": fileName,
                        "path": self.filePrefix + "/" + objectName,
                        "size": int(util.getFileSize(filePath)),
                        "md5": util.getFileMd5(filePath),
                        "frame_id":"",
                        "sensor":"",
                        "meta": {},
                        "anotations": {},
                    }
                    syncData.append(tmp)
                    i += 1
                    if self.debug:
                        per = (i * 100) // total
                        showText = filePath + " =====> " + objectName
                        process = "\r[UPLOAD_FILES][%3s%%]: %s" % (per, showText)
                        print(process)


                    j += 1
                    if j >= 1000:
                        j = 0
                        info = self.dataset.SyncDataToWeb(syncData)
                        if self.debug:
                            print(f"[UPLOAD_FILES] sync data to server, success:{info['succ']}, fail:{info['fail']}")

                        retInfo["succ"] += info["succ"]
                        retInfo["fail"] += info["fail"]
                        syncData = []

        if len(syncData) >= 0:#最后几个没同步的数据
            info = self.dataset.SyncDataToWeb(syncData)
            if self.debug:
                print(f"[UPLOAD_FILES] sync data to server, success:{info['succ']}, fail:{info['fail']}")
            retInfo["succ"] += info["succ"]
            retInfo["fail"] += info["fail"]

        return retInfo

    def UploadFileToDataset(self, filePath):
        """
        Upload one file to dataset service directly
        filePath: local file path
        """
        pathInfo = os.path.split(filePath)
        parentDir = os.path.abspath(os.path.join(pathInfo[0], ".."))
        objectName = pathInfo[0].replace(parentDir, "").strip("/").strip("\\") + "/" + pathInfo[1]
        self.__UploadFileToDataset(filePath, objectName)

        if self.debug:
            print(f"[UPLOAD_ONE_FILE][{filePath}  =====> {objectName}]")

        syncData = {
            "ref_id": "",
            "name": os.path.basename(filePath),
            "path": self.filePrefix+ "/" + objectName,
            "size": int(util.getFileSize(filePath)),
            "md5": util.getFileMd5(filePath),
            "frame_id":"",
            "sensor":"",
            "meta": {},
            "anotations": {},
        }

        info = self.dataset.SyncDataToWeb([syncData])
        if self.debug:
            print(f"[UPLOAD_ONE_FILE] sync data to server, success:{info['succ']}, fail:{info['fail']}")

        return info
        
    def __UploadFileToDataset(self, filePath, objectName):
        """
        Upload one file to OSS
        filePath: local file path
        objectName: OSS object name
        """
        if not self.datasetName:
            self.RiseException()
        return self.dataset.PutFileToDataset(objectName, filePath)

    def AddFile(self, filepath, objectName="", referId="", metaData={}, sensor="", frameId="") -> File:
        """
        Create a File object which is used for visualize data
        filePath: local file path
        objectName: OSS object name
        referId: referId
        metaData: metaData
        sensor: sensor
        frameId: frameId
        """
        if not self.datasetName:
            self.RiseException()

        if self.dataset.hostingMethod == self.dataset.HOSTING_METHOD_OWN_STORAGE:
            raise Exception(f"own hosting type dataset can not add files, you need use AddUrlFile method instead!")

        if not filepath:
            raise Exception(f"filepath must be set")

        file = File(filepath, objectName.strip("/"), self.filePrefix, self.dataset.endpoint, self.dataset.datasetType)
        self.fileList.append(file)
        if self.debug:
            print(f"[ADD FILE] filepath:{filepath}, objectName:{file.objectPath}")

        if referId != "":
            if not type(referId) is str:
                raise Exception(f"referId must be a str, {type(referId)} gavin")
            file.referId = referId

        if metaData != {}:
            file.metadata = MetaData(metaData)

        if sensor != "":
            if not type(sensor) is str:
                raise Exception(f"sensor must be a str, {type(sensor)} gavin")
            file.sensor = sensor

        if frameId != "":
            if not type(frameId) is str:
                raise Exception(f"frameId must be a str, {type(frameId)} gavin")
            file.frameId = frameId

        return file

    def AddUrlFile(self, url, referId="", metaData={}, sensor="", frameId="") -> File:
        """
        Create a UrlFile object which is used for visualize data
        url: fileUrl
        referId: referId
        metaData: metaData
        sensor: sensor
        frameId: frameId
        """
        if not self.datasetName:
            self.RiseException()

        if self.dataset.hostingMethod != self.dataset.HOSTING_METHOD_OWN_STORAGE:
            raise Exception(f"cloud-hosting type dataset can not add url files, you need use AddFile method instead!")

        if not url:
            raise Exception(f"url must be set")

        file = UrlFile(url,  self.dataset.datasetType)
        self.fileList.append(file)
        if self.debug:
            print(f"[ADD URL FILE] url:{url}")

        if referId != "":
            if not type(referId) is str:
                raise Exception(f"referId must be a str, {type(referId)} gavin")
            file.referId = referId

        if metaData != {}:
            file.metadata = MetaData(metaData)

        if sensor != "":
            if not type(sensor) is str:
                raise Exception(f"sensor must be a str, {type(sensor)} gavin")
            file.sensor = sensor

        if frameId != "":
            if not type(frameId) is str:
                raise Exception(f"frameId must be a str, {type(frameId)} gavin")
            file.frameId = frameId

        return file

    def Commit(self, commitId = ""):
        """
        commit a dataset data
        commitId: if set, this commit will add to the committed data before
        """
        if not self.datasetName:
            self.RiseException()

        db = LocalDb(commitId)
        if self.debug:
            if commitId:
                print(f"[COMMIT FILE] with commitId:{commitId}")
            else:
                print(f"[COMMIT FILE] without commitId!")

            print(f"[COMMIT FILE] begin insert data to local db")

        if len(self.fileList) <= 0:
            raise Exception("you should add 1 file at least")

        db.insertVal(self.fileList)

        if self.debug:
            print(f"[COMMIT FILE] end insert data to local db")

        db.close()

        self.commitFlag = True
        self.commitId = db.commitId
        return db.commitId

    def Upload(self, commitId = ""):
        """
        upload a committed dataset data and sync annotations to the testin-dataser server
        commitId: if set, this commit will be upload and snync
        """
        if not self.datasetName:
            self.RiseException()

        if commitId != "":
            if self.debug:
                print(f"[UPLOAD_VISUALIZE_FILE] upload visualize files to dataset with committed data: {self.commitId}")
            self.commitFlag = True
            self.commitId = commitId

        if not self.commitFlag:
            self.commitId = self.Commit(commitId)
            if self.debug:
                print(f"[UPLOAD_VISUALIZE_FILE] upload visualize files to dataset without commit, auto commit:{self.commitId}")

        if not self.commitId:
            self.commitId = self.Commit(commitId)
            if self.debug:
                print(f"[UPLOAD_VISUALIZE_FILE] upload visualize files to dataset without commit, auto commit:{self.commitId}")

        db = LocalDb(self.commitId)
        allData = db.fetchAll()
        total = len(allData)
        if total <= 0:
            raise Exception("you should add 1 file at least")

        if self.debug:
            print(f"[UPLOAD_FILES]files total count: {total}")

        syncData = []
        i = 0
        j = 0
        retInfo = {'succ': 0, 'fail': 0}
        for data in allData:

            if data["filepath"] != "" and data["objectPath"] != "":
                self.__UploadFileToDataset(data["filepath"], data["objectPath"])

            tmp = {
                "ref_id":data["referid"],
                "name":data["filename"],
                "path":data["osspath"],
                "size":data["filesize"],
                "md5":data["md5"],
                "frame_id":data["frame_id"],
                "sensor":data["sensor"],
                "meta":json.loads(data["metadata"]),
                "anotations":json.loads(data["labeldata"]),
            }
            syncData.append(tmp)
            i += 1
            j += 1

            if self.debug:
                per = (j * 100) // total
                showText = data['filepath'] + " =====> " + data['objectPath']
                process = f"\r[UPLOAD_FILES][{self.commitId}][%3s%%]: %s" % (per, showText)
                print(process)

            if i >= 100:
                i = 0
                info = self.dataset.SyncDataToWeb(syncData)
                if self.debug:
                    print(f"[UPLOAD_FILES] sync data to server, success:{info['succ']}, fail:{info['fail']}")
                retInfo["succ"] += info["succ"]
                retInfo["fail"] += info["fail"]
                syncData = []


        if len(syncData) >= 0:#最后几个没同步的数据
            info = self.dataset.SyncDataToWeb(syncData)
            if self.debug:
                print(f"[UPLOAD_FILES] sync data to server, success:{info['succ']}, fail:{info['fail']}")
            retInfo["succ"] += info["succ"]
            retInfo["fail"] += info["fail"]

        db.clearCache()

        return retInfo

    def GetData(self, offset=0, limit=100, metaKey="", metaValue="", label="", sensor=""):
        """
        search data from testin-dataset service by criteria
        offset: offset of the search result
        limit: numbers of the search result
        metaKey: which metadata key you would like to use to search 
        metaValue: set metadata value to your search  metadata key, if metaKey is not set, metaValue will be ignore
        label: search by label
        sensor: search by sensor
        all above criteria will be juxtaposition
        """
        if limit > 1000:
            raise Exception("limit must less than 1000")

        if (metaKey == "" and metaValue != "") or (metaKey != "" and metaValue == ""):
            raise Exception("both metaKey and metaValue are must set at the same time")

        return self.dataset.GetData(offset, limit, metaKey=metaKey, metaValue=metaValue, label=label, sensor=sensor, debug=self.debug)

    def GetFileAndLabel(self, fid="", referId=""):
        """
        search data from testin-dataset service by fid or ref_id
        fid: fid
        ref_id: referid you set when you upload data to testin-dataset service
        """
        if fid == "" and referId == "":
            raise Exception("fid or ref_id must be set")

        if fid:
            return self.dataset.GetFileAndLabelByFid(fid, debug=self.debug)

        return self.dataset.GetFileAndLabelByReferid(referId, debug=self.debug)





