#!/usr/bin/env python
# -*- coding: utf_8 -*-

import os
import re
import shutil
import copy
import json
import logging
from urllib import parse
from datetime import datetime
from pony.orm import *

from app import signalManager, config
from model.annex import Annex
from model.apps import Apps
from model.user import User
from model.app_logs import AppLogs
from model.build_logs import BuildLogs
from utils import sql_filter, ThreadMaker
from utils.epk import EpkApp

logger = logging.getLogger("AppsManager")

@ThreadMaker
def build_application(user, uuid):
    signalManager.actionAddBuildLog.emit(user, uuid, isMove=False)

class AppsManager(object):
    def __init__(self):
        super(AppsManager, self).__init__()

    def add(self, user, data):
        with db_session:
            editor = User.get(id=user)
            if not editor:
                return False, "current user is not exists"

            # result = Apps.select(app_name=data.get("app_name"), is_delete=False).count()
            # if result < 1:
            #     return False, "app_name has been exists."

            data.update({
                'app_icon': data["app_icon"].replace(config.get("UPLOAD_PATH"), ""),
                'create_by': editor,
                'create_at': datetime.now(),
                'update_by': editor,
                'update_at': datetime.now(),
            })

            app_files = []
            epk_path = ""
            if data.get("fileList"):
                app_files = data.get("fileList")
                data.pop("fileList")
                epk_path = data.get("epk_path")
                data.pop("epk_path")

            app = Apps(**data)
            commit()

            # 在EPK目录下生成JSON文件
            with open(os.sep.join([os.path.dirname(epk_path), "epk.json"]), "w") as f:
                json.dump(app.to_dict(exclude=["uuid", "create_at", "update_at", "delete_at"]), f)

            for a in app_files:
                Annex(app=app, title=os.path.basename(a), path=a, size=os.path.getsize(a), create_by=editor, create_at=datetime.now(), update_by=editor, update_at=datetime.now())
                flush()
            commit()

            app_info = {}
            params = { 'appName': app.app_name, 'appDir': epk_path, 'appVersion': app.app_version, 'output': os.path.dirname(epk_path) }
            if editor.role == "administrator" or editor.role == "community":
                params['algorithm'] ="h"
            epk = EpkApp(**params)
            app_info = epk.pack()

            epk_filename = os.sep.join([os.path.dirname(epk_path).replace(config.get("UPLOAD_PATH"), ""), "{}.epk".format(app.app_name)]).replace('\\', '/')
            if app_info:
                app_info['md5'] = str(app_info['md5'])

            result = BuildLogs(app=app, app_path=epk_filename, app_info=app_info, source=1, create_by=editor, create_at=datetime.now(), update_by=editor, update_at=datetime.now())
            commit()
            AppLogs(app_name=app.app_name, app_path=epk_filename, app_version=data.get("app_version"), app_info=app_info, create_by=editor, create_at=datetime.now())
            commit()

            return result, "add app {}".format("success" if result else "fail")

    def delete(self, user, uuid):
        with db_session:
            editor = User.get(id=user)
            if not editor:
                return False, "current user is not exists"

            result = Apps.get(uuid=uuid)
            if result:
                result.delete()

            return result, "delete app {}.".format("success" if result else "fail")

    def get(self, user, data):
        if not data.get("uuid"):
            return False, "app uuid can not be null"

        with db_session:
            editor = User.get(id=user)
            if not editor:
                return False, "current user is not exists"

            # 根据app查询应用，获取应用有哪些文件
            # 按格式创建文件夹，将这些文件移动到这个文件夹
            # 将这些零散文件进行打包
            # 更新数据库对应文件的路径

            app = Apps.get(uuid=data.get("uuid"))
            if not app:
                return None, "app not found"

            source_files = Annex.select().filter(app=app)
            if not source_files:
                return None, "apps file not found"

            dtNowString = datetime.now().strftime("%Y%m%d%H%M%S")
            dirname = "{}-{}-{}-{}".format(app.app_name, app.app_version, app.category, dtNowString)
            upload_dir = os.sep.join([config.get("UPLOAD_PATH"), config.get("EPK_DIR")])
            target_dir = os.sep.join([upload_dir, dirname])
            dest_dir = os.sep.join([target_dir, "src"])
            if not os.path.exists(dest_dir):
                os.makedirs(dest_dir)

            app_files = []
            for sf in source_files:
                target_file = sf.path
                if not os.path.exists(sf.path):
                    target_file = os.sep.join([config.get("UPLOAD_PATH"), sf.path])

                filename = os.path.basename(target_file)
                name, suffix = os.path.splitext(filename)
                name = re.sub(r"_\d{14}$", "", name)
                dst_file = os.path.normpath(os.sep.join([dest_dir, name + suffix]))
                shutil.copy(os.path.normpath(target_file), dst_file)
                app_files.append([sf.id, dst_file])

            with open(os.sep.join([target_dir, "epk.json"]), "w") as f:
                json.dump(app.to_dict(exclude=["uuid", "create_at", "update_at", "delete_at"]), f)

            # 打包成EPK文件
            app_info = {}
            params = { 'appName': app.app_name, 'appDir': dest_dir, 'appVersion': app.app_version, 'output': target_dir }
            if editor.role == "administrator" or editor.role == "community":
                params['algorithm'] = "h"
            epk = EpkApp(**params)
            app_info = epk.pack()
            app_info['md5'] = str(app_info['md5'])

            # 更新数据库对应文件路径
            # 将文件拷贝过去后，需要重新更新数据库文件记录
            epk_path = os.sep.join([target_dir.replace(config.get("UPLOAD_PATH"), ""), "{}.epk".format(app.app_name)]).replace('\\', '/')

            build = BuildLogs.get(app=app)
            if build:
                build.set(app_path=epk_path, app_info=app_info, update_by=editor, update_at=datetime.now())
                commit()

            # 新增一条AppLogs
            AppLogs(app_name=app.app_name, app_path=epk_path, app_version=app.app_version, app_info=app_info, create_by=editor, create_at=datetime.now())
            commit()

            return { 'app_name': app.app_name, 'app_path': epk_path }, "rebuild app {}.".format("success" if app_info else "fail")

    def getList(self, user, data):
        if not data or len(data) <= 0:
            return False, 0, "parameters can not be null."

        temp = copy.deepcopy(data)
        if 'pagenum' in temp:
            temp.pop('pagenum')
        if 'pagesize' in temp:
            temp.pop('pagesize')
        if 'scope_type' in temp:
            temp.pop('scope_type')

        with db_session:
            editor = User.get(id=user)
            if not editor:
                return False, "current user is not exists"

            if editor.role == "administrator":
                temp.update({"is_delete": False})
            else:
                temp.update({ "create_by": editor, "is_delete": False })

            if "scope_type" in data and data.get("scope_type") == "list":
                result = Apps.select().where(**temp).order_by(desc(Apps.create_at))
                temp = []
                for item in result:
                    temp.append(item.to_dict(only=["uuid", "app_name"]))
                return temp, len(temp), "get app {}.".format("success" if temp else "fail")
            elif "scope_type" in data and data.get("scope_type") == "distinct":
                result = select(p.category for p in Apps)
                temp = []
                for item in result:
                    temp.append(item)
                return temp, len(temp), "success"

            result = Apps.select().where(**temp).order_by(Apps.sort).sort_by(desc(Apps.create_at)).page(data.get("pagenum", 1), pagesize=data.get("pagesize", 10))
            count = Apps.select().where(**temp).count()

            if result and len(result):
                temp = []
                for item in result:
                    t = item.to_dict(with_collections=True, related_objects=True, exclude=["app_annex", "app_download", "is_delete", "delete_by", "delete_at"])
                    t.update({
                        "app_build_log": item.app_build_log.to_dict(exclude=["is_delete", "delete_by", "delete_at", "create_by", "update_by"]) if item.app_build_log else None,
                        "create_by": item.create_by.to_dict(only=["uuid", "username"]),
                        "update_by": item.update_by.to_dict(only=["uuid", "username"]),
                        "create_at": item.create_at.strftime("%Y-%m-%d %H:%M:%S") if item.create_at else None,
                        "update_at": item.update_at.strftime("%Y-%m-%d %H:%M:%S") if item.update_at else None,
                    })
                    temp.append(t)
                result = temp
            return result, count, "get app {}.".format("success" if result else "no data")

    def update(self, user, uuid, data):
        # 当参数为空时，直接返回错误
        if len(data) <= 0 or (len(data.keys()) == 1 and "id" in data):
            return False, "app can not be null."

        with db_session:
            editor = User.get(id=user)
            if not editor:
                return False, "current user is not exists"

            result = Apps.get(uuid=uuid)
            if not result:
                return False, "app not found"

            if data.get("app_files"):
                app_files = data.get("app_files")
                for a in app_files:
                    Annex(app=result, title=a.get("filename"), path=a.get("filepath"), size=a.get("filesize"), create_by=editor, create_at=datetime.now(), update_by=editor, update_at=datetime.now())
                    flush()
                commit()
                data.pop("app_files")

            if data.get("app_icon"):
                condition = {
                    'update_by': editor,
                    'update_at': datetime.now()
                }
                if data.get("app_icon").get("filename"):
                    condition.update({"title": data.get("app_icon").get("filename")})
                if data.get("app_icon").get("filepath"):
                    condition.update({"path": data.get("app_icon").get("filepath")})
                if data.get("app_icon").get("filesize"):
                    condition.update({"size": data.get("app_icon").get("filesize")})

                result.app_icon.set(**condition)
                commit()
                data.pop("app_icon")

            result.set(update_at=datetime.now(), update_by=editor, **data)
            commit()

            build_application(user, str(result.uuid))

            return result, "update app {}.".format("success" if result else "fail")

    def build(self, files, data):
        with db_session:
            user = User.get(uuid=data['access_key'])
            if not user:
                return False, "user does not exists"

            if data.get("access_key"):
                data.pop("access_key")

            data.update({
                'create_by': user,
                'create_at': datetime.now(),
                'update_by': user,
                'update_at': datetime.now(),
            })

            app = Apps(**data)
            commit()

            dir_format = "{}-{}-{}".format(app.app_name, app.app_version, datetime.now().strftime("%Y%m%d%H%M%S"))
            upload_dir = os.sep.join([config.get("UPLOAD_PATH"), "evueapps"])
            target_dir = os.sep.join([upload_dir, user.account, dir_format])
            dest_dir = os.sep.join([target_dir, "src"])
            if not os.path.exists(dest_dir):
                os.makedirs(dest_dir)

            for target_file in files:
                filename = os.path.basename(target_file)
                name, suffix = os.path.splitext(filename)
                name = re.sub(r"_\d{14}$", "", name)
                dst_file = os.path.normpath(os.sep.join([dest_dir, name + suffix]))
                shutil.copy(os.path.normpath(target_file), dst_file)

                Annex(app=app, title=filename, path=dst_file.replace(config.get("UPLOAD_PATH"), ""), size=os.path.getsize(dst_file), create_by=user, create_at=datetime.now(), update_by=user, update_at=datetime.now())
                flush()
            commit()

            # 打包成EPK文件
            app_info = {}
            params = { 'appName': app.app_name, 'appDir': dest_dir, 'appVersion': app.app_version, 'output': target_dir }
            if user.role == "administrator" or user.role == "community":
                params['algorithm'] = "h"
            epk = EpkApp(**params)
            app_info = epk.pack()
            app_info['md5'] = str(app_info['md5'])

            # 更新数据库对应文件路径
            # 将文件拷贝过去后，需要重新更新数据库文件记录
            epk_path = os.sep.join([target_dir.replace(config.get("UPLOAD_PATH"), ""), "{}.epk".format(app.app_name)]).replace('\\', '/')

            build = BuildLogs.get(app=app)
            if build:
                build.set(app_path=epk_path, app_info=app_info, update_by=user, update_at=datetime.now())
                commit()
            else:
                BuildLogs(app=app, app_path=epk_path, app_info=app_info, source=2, create_by=user, create_at=datetime.now(), update_by=user, update_at=datetime.now())
                commit()

            # 新增一条AppLogs
            AppLogs(app_name=app.app_name, app_path=epk_path, app_version=app.app_version, app_info=app_info, create_by=user, create_at=datetime.now())
            commit()

            with open(os.sep.join([target_dir, "epk.json"]), "w") as f:
                json.dump(app.to_dict(exclude=["uuid", "create_at", "update_at", "delete_at"]), f)

            return { 'app_name': app.app_name, 'app_file': "{}.epk".format(app.app_name), 'app_url': parse.urljoin(config['UPLOAD_SERVER'], epk_path) }, "application build {}.".format("success" if app_info else "fail")

appsManager = AppsManager()
