#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from inc import CliTool
import os, json, sys

##获取发布的idc机器列表
def get_idc_host_file(idc, app = None, is_web = False, only_sw = False):
    idc = idc.lower()
    ##腾讯云
    if idc == 'tc' and app in ['greatboss']:
        return 'hosts/hosts-tc-us-vpc.txt'

    ##阿里云
    if idc == 'aliyun':
        return 'hosts/hosts-honor-vpc-sw.txt' if is_web is True or only_sw is True else 'hosts/hosts-honor-vpc.txt'
    if idc == 'earth':
        return 'hosts/hosts-earth-sw.txt' if is_web is True or only_sw is True else 'hosts/hosts-earth.txt'
    if idc == 'mjs':
        return 'hosts/hosts-mjs-vpc.txt'
    if idc == 'plw':
        return 'hosts/hosts-plw.txt'
    if idc == 'wms':
        return 'hosts/hosts-wms.txt'
    if idc == 'msg':
        return 'hosts/hosts-msg.txt'
    if idc == 'fd':
        return 'hosts/hosts-fd.txt'
    if idc == 'ssr':
        return 'hosts/hosts-node-ssr.txt'
    if idc == 'eberp':
        return 'hosts/hosts-easyboss.txt'
    if idc == 'ykt':
        return 'hosts/hosts-ykt.txt'
    
    ##京东云
    if idc == 'jddz':
        return 'hosts/hosts-zndz-i.txt'
    if idc == 'jdms':
        return 'hosts/hosts-honor-i.txt'
    if idc == 'jdop':
        return 'hosts/hosts-jd-op.txt'
    if idc == 'jdopry':
        return 'hosts/hosts-jdry-op-i.txt'
    if idc == 'jdopms':
        return 'hosts/hosts-jdms-op-i.txt'
    if idc == 'jdbk':
        return "hosts/hosts-honor-jdbk.txt"
    if idc == 'jdbkod':
        return "hosts/hosts-honor-jdbk-od.txt"
    if idc == 'jddzfx':
        return 'hosts/hosts-jddz-fx.txt'
    if idc == 'jdmsfx':
        return 'hosts/hosts-jdms-fx.txt'
    if idc == 'jdfx':
        return 'hosts/hosts-jd-fx.txt'
    if idc == 'jdapple':
        return 'hosts/hosts-apple.txt'
    if idc == 'jdgold':
        return 'hosts/hosts-gold.txt'

    ##抖店云
    if idc == 'dddz':
        return 'hosts/hosts-honor-ddy-dz.txt'
    if idc == 'ddms':
        return 'hosts/hosts-honor-ddy-ms.txt'
    if idc == 'ddbk':
        return 'hosts/hosts-honor-ddy-tbk.txt'
    if idc == 'ddry':
        return 'hosts/hosts-honor-ddy-ry.txt'
    if idc == 'ddfy':
        return 'hosts/hosts-honor-ddy-fy.txt'
    
    ##聚石塔
    if idc == 'jstacn':
        return 'hosts/hosts-jst-acn.txt'
    if idc == 'jsttbk':
        return 'hosts/hosts-jst-tbk.txt'
    if idc == 'jsttbdz':
        return 'hosts/hosts-jst-tbdz.txt'
    if idc == 'jsttbms':
        return 'hosts/hosts-jst-tbms.txt'
    if idc == 'jsterpds':
        return 'hosts/hosts-jst-erp-ds.txt'
    
    ##境外
    if idc == 'cbpc':
        return 'hosts/hosts-cb-pc.txt'
    if idc == 'gberp':
        return 'hosts/hosts-tc-us-vpc.txt'
    
##校验前后分销项目是否合法
def verify_febe_app_legal(version_file, pub_app):
    # 检查文件是否存在, 不存在默认允许
    if os.path.exists(version_file) is False:
        return True

    app_mode = None
    with open(version_file, 'r') as file:
        # 解析 JSON 数据
        data = json.load(file)
        # 获取appName的值
        app_mode = data.get('appMode', None)
    
    if app_mode is None:
        return True

    app_name_list = {
        'earth': ['earth'],
        'desktop': ['earth-desktop'],
        'dianjiufang': ['earth-dianjiufang'],
        'easyboss': ['dm-erp'],
        'greatboss': ['greatboss'],
        'dz': ['fetch-bank-v2-dz', 'honor-ksdz-v2', 'honor-xhs-dz', 'honor-xhs-dz-order-v2', 'honor-sphxd-dz'],
        'tbk': ['fetch-bank-v2-tbk', 'honor-xhs-tbk', 'honor-xhs-tbk-order-v2'],
        'ry': ['fetch-bank-v2-ry', 'honor-xhs-ry', ' honor-xhs-ry-order-v2'],
    }
    real_app_mode = next((key for key, value in app_name_list.items() if pub_app in value), None)

    if real_app_mode is None:
        return True
    
    if real_app_mode !=  app_mode:
        print("发布的app {} 和编译生产的appMode {} 不一致, 中断发布".format(pub_app, app_mode))
        return False
    
#前后端分析项目，之发布sw机器
def is_ay_febe_app(idc, app):
    if idc == 'aliyun':
        return app in [
            'honor-ksdz-v2', 
            'fetch-bank-v2', 
            'fetch-bank-v2-ms', 
            'fetch-bank-v2-ds', 
            'fetch-bank-v2-dz', 
            'fetch-bank-v2-gold', 
            'fetch-bank-v2-ry', 
            'fetch-bank-v2-tbk', 
            'fetch-bank-v2-ds', 
            'fetch-bank-v2-dmds', 
            'cb-bmp',
            'honor-xhs-dz', 
            'honor-xhs-ry', 
            'honor-xhs-tbk',
            'honor-xhs-dz-order-v2',
            'honor-xhs-tbk-order-v2',
            'honor-xhs-ry-order-v2',
            'honor-sphxd-tbk',
            'honor-sphxd-order-dz',
            'honor-sphxd-order-tbk',
            'honor-sphxd-order-ry',
            'honor-ksdz-order-v2',
            'earth-h5-web', 
            'easyboss-h5-web', 
            'honor-ap',
            'honor-dd-ir',
            'honor-dddz-ir',
        ]
    
    if idc == 'ddms':
        return app in [
            'honor-dd-ir'
        ]
    
    if idc == 'dddz':
        return app in [
            'honor-dddz-ir'
        ]

##检查app部署类型，返回febe和classic
def get_app_deploy_type(code_path):
    for directory in ['api', 'web']:
        directory_path = os.path.join(code_path, directory)
        if not os.path.exists(directory_path):
            print("目录 {} 不存在，返回 classic ".format(directory))
            return 'classic'
    
    return 'febe'

def get_pubish_module(app, env):
    if env == 'pre':
        if app in [
            'greatboss', 'dm-erp', 'gwms',
            'joy-tbk', 'joy-order-tbk',
            'proxy-center', 'order-print',
            'honor-1688-ds-api'
        ]:
            return 'www/{}-pre'.format(app)

    if app in ['tbk-crm']:
        return 'www/coupon-pre' if env == 'pre' else 'www/coupon'
     
    return 'www/{}'.format(app)

def is_joa_app(app):
    return app in [
        ##阿里云系列
        'honor-zfb',
        'jcrm',
        'honor-zfb-order',
        'proxy-center',
        'sms-center',
        'honor-ks-v2',
        'honor-ks-move',
        'honor-ksdz-v2',
        'honor-ksdz-move',
        'honor-ks-order',
        'honor-ks-order',
        'honor-ksdz-order-v2',
        
        ##京东云系列
        'honor-xhs-order',
        'order-print',
        'honor',
        
        ##抖店系列
        'honor-dd-v2',
        'honor-dddz-v2'
    ]
    
##获取代码的代码路径
def get_publish_code_path(app, is_intra = False):
    if is_intra is True:
        return '/alidata/www/{}/'.format(app)
    
    base_dir = CliTool.workspace()
    default_path = '{}.git'.format(app)
    code_path = {
        'honor': 'honor.master',
        'honor-1688-dm-ds': 'honor-1688-ds-api',
        'honor-1688-ds-tbk-api': 'honor-1688-ds-api',
        'fetch-bank': 'fetch-bank-v2.git/api',
        'easyboss-h5': 'dm-erp-applet.git/build/easyboss-h5',
        'greatboss-h5': 'dm-erp-applet.git/build/greatboss-h5',
    }.get(app, default_path)
    
    return base_dir + code_path + '/'
    
def get_publish_ext_env(env, app):
    if env == 'pub' and app == 'fetch-bank':
        env += ',jc-pub'
        return env
    
    if env != 'real':
        return env
    
    if app in ['earth', 'earth-desktop']:
        env += ',jst-real'

    if app in ['order-print']:
        env += ',ry-real,ms-real,tbk-real,df-real'

    env += ',joa-real'

    return env


##build下发布的排除文件    
def build_exclude_ext(ext_mode = None, exclude_atjmax = False):
    exclude_atjmax_exclude = ' --exclude=/api/app/libs/controller/atjmax/* --exclude=/api/app/views/page/atjmax/* '
    exclude_atjmax_exclude += ' --exclude=/app/libs/controller/atjmax/* --exclude=/app/views/page/atjmax/* '

    exclude_ext = ''
    if ext_mode == 'api':
        exclude_ext += ' --include=/api/ --exclude=/web/* '

    if ext_mode == 'web':
        exclude_ext += ' --exclude=/api/* --include=/web/dist/ '

    if exclude_atjmax is True:
        exclude_ext += exclude_atjmax_exclude

    return exclude_ext

#build发布命令
def build_command_tpl(del_type, pwd_file, exclude_ext, pubish_code_path):
    exclude_ext = CliTool.excludeFile() + exclude_ext
    cmd_tpl = 'rsync -%s --delay-updates {} --stats --port=%s --password-file={} --timeout=120 {} {} www@%s::%s/'.format(del_type, pwd_file, exclude_ext, pubish_code_path)
    return cmd_tpl

#发布sw机器的web部分
def pubish_sw(sw_hosts, code_type, mode_type, pwd_file, pubish_code_path):
    if code_type == 'api':
        return 
    
    if not sw_hosts:
        print("sw 机器为空")
        exit()
        
    exclude_ext_web =  build_exclude_ext(ext_mode='web')
    Ct = CliTool()
    cmd_tpl_web_no_del = build_command_tpl('', pwd_file, exclude_ext_web, pubish_code_path)
    print("\n\n正在发布[SW]机器 and no --del ...")
    Ct.publish(sw_hosts, mode_type, cmd_tpl_web_no_del)
    
    if code_type == 'web':
        cmd_tpl_web = build_command_tpl('--del', pwd_file, exclude_ext_web, pubish_code_path)
        print("\n\n正在发布[SW]机器")
        Ct.publish(sw_hosts, mode_type, cmd_tpl_web)
        
        
##开始发布
def start_publish(idc, app, mode, env, is_intra = False, code_type = None, is_web = False, force_port = None):
    """
    发布命令的开始入口
    
    args
    idc: str, 发布的idc机器
    app: str, 发布的app
    env : str, 发布的环境，根据host里面的hosts文件配置：real, pre, test, 等
    is_intra: bool, 是否是内网
    mode: str, 发布的模式可用为：real, rdtest, test
    is_web: bool, 是否是前后端分离项目
    code_type: str, 项目类型，可用为：api, web
    force_port: int, 强制指定端口
    """

    ##定义rsync基本命令
    pwd_file = '/home/porsche/publish/tjds1.passwd' if is_intra is True else '/Users/tangjianhui/Documents/publish/tjds1.passwd'
    pubish_code_path = get_publish_code_path(app, is_intra)
    Ct = CliTool()
    ##获取APP部署类型
    deploy_type = get_app_deploy_type(pubish_code_path)

    ## febe检查根目录中是否存在.htaccess文件
    if deploy_type == 'febe':
        if os.path.exists(os.path.join(pubish_code_path, '.htaccess')) is False:
            print("文件 .htaccess does not exist.")
            return
        
        fs_dist_dir = {
            'honor-1688-ds': 'web/build',
        }.get(app, 'web/dist')
        if os.path.exists(os.path.join(pubish_code_path, '{}/index.html'.format(fs_dist_dir))) is False and code_type != 'api':
            print("文件 web/dist/index.html does not exist.")
            return
    
    ##检查api目标是否存在
    if code_type in ['web', 'api'] and deploy_type != 'febe':
        print("非前后端分离项目不支持api发布")
        return
    
    ##检查本地发布pub和real分支是否合法
    if (is_intra is False and env in ['pub', 'real']) and mode == 'real':
        realLocalSrc = pubish_code_path if os.path.exists(pubish_code_path + ".git") else pubish_code_path + 'api/'
        res = os.popen("cd " + realLocalSrc + " && git branch -a | grep '*' | awk '{print $2}'")
        branch = res.read().strip("\n")

        if any(keyword.lower() in branch.lower() for keyword in ['real', 'master', 'revert']) is False:
            print(("清检查分支: {} 是否为正式发布分支? 正式发布分支名称需包含 real、master、revert等关键词".format(branch)))
            exit()

    ##发布目标目录
    module = get_pubish_module(app, env)
    
    ##获取多软件分身的分身应用，仅发布sw机器即可
    is_only_pub_sw = is_ay_febe_app(idc, app)

    ##获取机器列表
    file_host_name_path = '{}/{}'.format(os.path.dirname(sys.argv[0]), get_idc_host_file(idc, app, is_web, is_only_pub_sw))

    if not os.path.exists(file_host_name_path):
        print("Host file {} does not exist.".format(file_host_name_path))
        return

    ##所有的机器列表
    ips_map = CliTool.getIpsMap(file_host_name_path, isIntra = is_intra, defaultPort = 8873, defaultModule = module, forePort=force_port)
    
    ##待发布的环境
    all_env = get_publish_ext_env(env, app)
    env_list = CliTool.getEnvList(all_env, ips_map, is_intra)

    ##获取发布的机器列表
    ips = {}
    for ev in env_list:
        if ev not in ips_map:
            continue
        ips = dict(ips, **ips_map[ev])

    ##sw机器
    ips_sw = {key: value for key, value in ips.items() if '-sw' in key}
            
    ##发布joa机器
    if is_joa_app(app) and 'joa-real' in ips_map and env == 'real' and mode == 'real':
        joa_ips = ips_map['joa-real']
        joa_ext = build_exclude_ext(ext_mode='api', exclude_atjmax=False)
        cmd_joa_tpl = build_command_tpl('--del', pwd_file, joa_ext, pubish_code_path)
        print("正在发布joa机器 ...")
        Ct.publish(joa_ips , mode, cmd_joa_tpl)
        
    ##发布sw机器(只发布web部分，多软件分身的分身应用如：dianbafang，ksdz等，只发布sw机器)
    if is_web is True:
        pubish_sw(ips_sw, code_type, mode, pwd_file, pubish_code_path)
        return
    
    ##如果是前后端分离的项目先发布sw机器的web部分
    if deploy_type == 'febe' and code_type is None and mode == 'real' and env == 'real':
        pubish_sw(ips_sw, code_type, mode, pwd_file, pubish_code_path)

    ##其他正常发布(暂时手动控制白名单为主)
    exclude_atjmax = True if is_joa_app(app) and env not in ['pre', 'pub', 'test', 'joa-real'] else False
    default_ext = build_exclude_ext(ext_mode=code_type, exclude_atjmax=exclude_atjmax)
    cmd_tpl_all = build_command_tpl('--del', pwd_file, default_ext, pubish_code_path)
    
    syntax_check = False if is_intra is True else True
    Ct.publish(ips, mode, cmd_tpl_all, is_intra, php_syntax_check = syntax_check)
   
def start_publish_zk(idc, zk, mode, env, is_intra = False, force_port = None):
    ##定义rsync基本命令
    pwd_file = '/home/porsche/publish/tjds2.passwd'
    
     ##获取机器列表
    file_host_name_path = '{}/{}'.format(os.path.dirname(sys.argv[0]), get_idc_host_file(idc=idc))
    if not os.path.exists(file_host_name_path):
        print("Host file {} does not exist.".format(file_host_name_path))
        return
    
    ips_map = CliTool.getIpsMap(file_host_name_path, isIntra = is_intra, defaultPort=8875, defaultModule = 's', forePort=force_port)
    
    ##待发布的环境
    env_list = CliTool.getEnvList(env, ips_map, is_intra)
    
    ##获取发布的机器列表
    ips = {}
    for env in env_list:
        ips = dict(ips, **ips_map[env])
        
    ##Zk文件
    if zk in ['xiuxiu-node-api', 'ai-ps', 'node-video-v2']:
        zkFile = 'Zk.' + zk  + '.json'
    else:
        zkFile = 'Zk.' + zk  + '.inc'
    
    pubish_code_path = '/alidata/s/' + zkFile
    cmdTpl = 'rsync -%s --delay-updates --del --stats --port=%s --password-file=' + pwd_file + ' --timeout=30 ' + pubish_code_path + ' root@%s::%s/' + zkFile
    Ct = CliTool()
    Ct.publish(ips, mode, cmdTpl)

     
if __name__ == '__main__':
    pass