#!/usr/bin/env python3
# -*- coding:utf-8 -*-
from libs.inc import CliTool
from conf.app_const import *
import sys, os
import subprocess
from typing import Optional

idc = sys.argv[1]
app = sys.argv[2]
isIntra = (sys.argv[3] == 'intra')
pubEnv = sys.argv[4]

#编译node参数
npmCmd = sys.argv[5]
onlyPubApi =  (sys.argv[5] == 'api')
onlyPubWeb = False

mode = sys.argv[5]
timeout = '120'

argvNum = len(sys.argv)
if onlyPubApi is True:
    npmCmd = False
    mode = sys.argv[6]

if isIntra is True:
    localPwdFile = '/home/porsche/publish/tjds1.passwd'
else:
    localPwdFile = '/Users/tangjianhui/Documents/publish/tjds1.passwd'

def getDictKeyByValue(app, distMap):
    for realRepo, apps in distMap.items():
        if app in apps:
           return realRepo
        
    return False


def get_nvm_path() -> Optional[str]:
    """
    从 .bashrc 文件中获取 NVM 路径
    
    Returns:
        NVM 路径或 None（如果未找到）
    """
    bashrc_path = os.path.expanduser("~/.bashrc")
    
    if not os.path.exists(bashrc_path):
        return None
    
    try:
        nvm_dir = None
        
        with open(bashrc_path, 'r', encoding='utf-8') as f:
            content = f.read()
            
            # 查找 NVM_DIR 环境变量设置
            for line in content.split('\n'):
                line = line.strip()
                if line.startswith('export NVM_DIR='):
                    # 提取引号内的值
                    nvm_dir_value = line.split('=', 1)[1].strip('\'"')
                    
                    # 处理 $HOME 变量
                    if nvm_dir_value.startswith('$HOME'):
                        nvm_dir = nvm_dir_value.replace('$HOME', os.path.expanduser('~'))
                    else:
                        nvm_dir = os.path.expanduser(nvm_dir_value)
                    
                    break
            
            # 如果找到了 NVM_DIR，检查 nvm.sh 是否存在
            if nvm_dir:
                nvm_sh_path = os.path.join(nvm_dir, 'nvm.sh')
                if os.path.exists(nvm_sh_path):
                    return nvm_sh_path
                else:
                    print(f"NVM_DIR 已设置为 {nvm_dir}，但 nvm.sh 文件不存在")
            
            # 备用方案：直接查找 source nvm.sh 的行
            for line in content.split('\n'):
                line = line.strip()
                if ('source' in line or '\\.' in line) and 'nvm.sh' in line:
                    # 处理类似 [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" 的格式
                    if '$NVM_DIR' in line and nvm_dir:
                        nvm_sh_path = os.path.join(nvm_dir, 'nvm.sh')
                        if os.path.exists(nvm_sh_path):
                            return nvm_sh_path
                    
                    # 处理直接路径
                    import re
                    match = re.search(r'["\']([^"\']*nvm\.sh)["\']', line)
                    if match:
                        nvm_path = match.group(1)
                        nvm_path = nvm_path.replace('$NVM_DIR', nvm_dir if nvm_dir else '')
                        nvm_path = os.path.expanduser(nvm_path)
                        if os.path.exists(nvm_path):
                            return nvm_path
                            
    except Exception as e:
        print(f"读取 .bashrc 时发生错误: {e}")
    
    return None


def exec_command(cmd: str, output_resp: bool = False) -> str:
    """
    执行bash命令并返回输出结果
    
    Args:
        cmd: 要执行的命令
        output_resp: 是否实时打印输出
    
    Returns:
        命令的输出结果
        
    Raises:
        subprocess.CalledProcessError: 命令执行失败时抛出
    """
    # 获取 NVM 路径
    nvm_path = get_nvm_path()
    if not nvm_path:
        # 如果在 .bashrc 中找不到，尝试默认路径
        default_nvm_path = os.path.expanduser("~/.nvm/nvm.sh")
        if os.path.exists(default_nvm_path):
            nvm_path = default_nvm_path
        else:
            raise FileNotFoundError("无法找到 NVM 安装路径，请检查 .bashrc 配置")
    
    output_str = ''
    command = f"source {nvm_path} && {cmd}"
    
    try:
        with subprocess.Popen(
            ['/bin/bash', '-c', command],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            universal_newlines=True,
            bufsize=1
        ) as process:
            
            # 实时读取输出
            for line in process.stdout:
                line = line.strip()
                if line:
                    if output_resp:
                        print(line)
                    else:
                        output_str += line + '\n'
            
            # 等待进程结束并检查返回码
            return_code = process.wait()
            if return_code != 0:
                raise subprocess.CalledProcessError(return_code, command)
                
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
        raise
    except Exception as e:
        print(f"执行命令时发生错误: {e}")
        raise
    
    return output_str.strip()


if isIntra is True:
    localSrc = '/alidata/www/' + app + '/'
else:
    ##根据app或者真实的地址
    repo =  get_app_repo_map(app)
    localSrc = CliTool.workspace() + repo + '.git/'
    if os.path.isdir(localSrc) is False:
        print('目录 {}.git 未找到， 非有效的程序目录'.format(repo))
        exit()

buildWeb = npmCmd in ['npmbd', 'npmi', 'npmbd-web', 'npmi-web']
isWebOnly = app in [
    APP_TMS_MOBILE, 
    APP_TMS_APP, 
    APP_YKT_APP, 
    APP_MKT_EDITOR, 
    APP_DD_MOBILE, 
    APP_DDDZ_MOBILE, 
    APP_DD_OD_MOBILE, 
    APP_DDDZ_OD_MOBILE,
    APP_DS_DD_OD_MOBILE,
    APP_BARCODE_EDITOR,
    APP_SPHXD_MOBILE,
    APP_DDY_DDDZ_OD_IR,
    APP_DDY_DD_OD_IR,
    APP_JOY_ORDER_IR, 
    APP_JOY_ORDER_IR_DZ
]

## check nvm version
nvmVersion = exec_command('nvm -v')
if '0.39' not in nvmVersion:
    print("NVM 未安装，请先安装nvm")
    exit()

## check node -version
nodeVersion = 'v22'
checkResp = exec_command("nvm ls {}".format(nodeVersion))
if 'N/A' in checkResp:
    print("Node.js {} is not installed. Please install it using NVM and try again.".format(nodeVersion))
    exit()

if isWebOnly is True and buildWeb is False and npmCmd != 'web':
    print('前端发布命令npmbd/npmi/npmbd-web/npmi-web')
    exit()

if buildWeb is True:
    feGitDir = {
        APP_TMS_MOBILE: 'mobile',
        APP_TMS_APP: 'tms-applet',
    }.get(app, 'web')
    
    baseCmd = 'export NODE_OPTIONS="--max-old-space-size=8192"; cd ' + localSrc + feGitDir

    buildCmd = get_yarn_build_cmd(app)
    print(buildCmd)
    ##零时兼容默认build命令，之前全部废除
    if buildCmd is False:
        print("app {}编译命令为False,请检查".format(app))
        exit()


    for tag in ['dz', 'ry', 'tbk', 'digshow', 'desktop', 'gold', 'greatboss', 'easyboss']:
        if app in [APP_EARTH_DESKTOP_CS]:
            continue
         
        if tag in app.lower() and tag not in buildCmd.lower():
            print("app [{}] 中包含({})关键字，但是编译命令（{}）不存在，清检查编译命令是否正确".format(app, tag, buildCmd))
            exit()

    if pubEnv == 'pre':
        buildCmd = {
            APP_EASYBOSS: 'build:easyboss-pre',
        }.get(app, buildCmd)

        if app in [APP_YKT, APP_YKT_DIGSHOW,APP_YKT_AI]:
            buildCmd += '-pre'
    
    
    exec_command("nvm use {}".format(nodeVersion))
    cmd = '{} && yarn && VITE_BUILD_ENV={} yarn {}'.format(baseCmd, pubEnv, buildCmd)
    print("start run command " + cmd)
    if buildCmd in ['xxweb:build', 'xxweb:build.digshow']:
        print("start xxweb:build for os.system ")
        os.system(cmd)
    else:
        CliTool.processCmd(cmd)

    if(app in [APP_EARTH, APP_ERP_DESKTOP, APP_ERP_DIANJIUFANG, APP_ERP_BAIKE, APP_EARTH_MXZ]):
        cmd = baseCmd + ' && rm -rf dist distv1 && /bin/bash scripts/bundle.sh '
        print("start run command " + cmd)
        CliTool.processCmd(cmd)

        cmd = baseCmd + ' && mv dist disttmp && mv disttmp/* ./ && rm -rf disttmp'
        print("start run command " + cmd)
        CliTool.processCmd(cmd)

    if(app in [APP_YKT, APP_YKT_DIGSHOW, APP_YKT_AI, APP_MKT_EDITOR, APP_BARCODE_EDITOR]):
        cmd = baseCmd + ' && rm -rf dist && mv packages/xiuxiu-web/dist ./'
        print("start run command " + cmd)
        CliTool.processCmd(cmd)

    mode = sys.argv[6]

###erpchat 前端增量发布
isIncPub = False
if npmCmd in ['npmbd-web', 'npmi-web', 'web'] or isWebOnly is True:
    if app == APP_ERP_CHAT and pubEnv == 'pre':
        isIncPub = True

    onlyPubWeb = True
    mode = sys.argv[6]

if (npmCmd is False and onlyPubApi is False) or isWebOnly is True:
    fsDIstDir = {
        APP_TMS_MOBILE: 'mobile/dist',
        APP_TMS_APP: 'tms-applet/dist/build/h5',
        APP_ACN_DS_v2: 'web/build',
    }.get(app, 'web/dist')

    distDir = localSrc + fsDIstDir
    distv1Dir = localSrc + 'web/distv1'
    if os.path.isfile(distDir + '/index.html') is False:
        print(distDir + ' 编译文件不存在!')
        exit()
        
    if (app in [APP_EARTH, APP_ERP_DESKTOP, APP_ERP_DIANJIUFANG, APP_EASYBOSS, APP_ERP_BAIKE, APP_EARTH_MXZ]) and os.path.isfile(distv1Dir + '/index.html') is False:
        print(distv1Dir + ' web/distv1 编译文件不存在!')
        exit()

if onlyPubApi is True:
    apiDir = localSrc + 'api'
    if os.path.exists(apiDir) is False:
        print(apiDir + ' api 目录不存在!')
        exit()

# Get module based on app and pubEnv
module = get_app_module(app, pubEnv)
     
if pubEnv in ['pub', 'real'] and mode == 'real' and onlyPubApi is True:
    realLocalSrc = localSrc if os.path.exists(localSrc + ".git") else localSrc + '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()

    if is_php8_idc(idc=idc) is False:
        ##检查git仓库的子模块是否是最新的远程节点
        is_valid, outdated_submodules, has_submodules = CliTool.checkGitSubmodules(realLocalSrc)
        if has_submodules:
            if not is_valid:
                print("\n[错误] 以下子模块不是最新的远程节点，请更新后再发布:")
                for sub in outdated_submodules:
                    print("  - {}: 当前 {} != 远程 {}".format(sub['path'], sub['current'], sub['remote']))
                print("")
                exit()
        else:
            print("[提示] 该仓库不存在子模块，跳过子模块检查")

pubPort = {
    APP_JOY_ORDER: '3389',
    APP_JOY_ORDER_RY: '3389',
    APP_JOY_GOLD: '3389',
}.get(app, '8873')

##获取对应的hosts文件
file_host = get_idc_host_file(idc=idc)
if file_host is False:
    print("app {} 对应的发布文件不存在, 请检查".format(app))
    exit()
    
##完整的文件路径  
file_full_path = "{}/conf/hosts/{}".format(os.path.dirname(sys.argv[0]), file_host)
if os.path.isfile(file_full_path) is False:
    print("hosts file {} 不存在,  请检查".format(file_full_path))
    exit()
    
ipsMap = CliTool.getIpsMap(file_full_path, isIntra, pubPort, module)

if (pubEnv == 'pre'):
    switcher = {
        APP_EARTH: 'pre,jst-pre',
    }
    pubEnv = switcher.get(app, 'pre')

envList = CliTool.getEnvList(pubEnv, ipsMap, isIntra)

ips = {}
for env in envList:
    ips = dict(ips, **ipsMap[env])

if onlyPubWeb is False: 
    htaccess = os.path.isfile(localSrc + '.htaccess')
    htaccessPath = 'api/root.htaccess'
    existsHtaccessPath = os.path.isfile(localSrc + htaccessPath)

    if existsHtaccessPath is True:
        cmd = ('cd ' + localSrc + ' && rm -rf root.htaccess .htaccess && cp ' + htaccessPath + ' ./ && mv root.htaccess .htaccess')
        print("start process zip command: " + cmd + "\n")
        outputStr = CliTool.execCommand(cmd)
        print(outputStr)
    elif htaccess is False:
        print(htaccessPath + ' 文件不存在，请先添加!')
        exit()
    else:
        print("root.htaccess not exists but .htacces exists")

rsync_del='--del' if isIncPub is False else ''
cmdTpl = 'rsync -%s --delay-updates {} --stats --port=%s --password-file={} --timeout={}'.format(rsync_del, localPwdFile, timeout)

sshCmdTpl = 'rsync -%s -e ssh --delay-updates --del --stats --port=%s --timeout=' + timeout

cmdTplExt = ''
if onlyPubWeb is True:
    if app in [APP_TMS_MOBILE]:
        cmdTplExt += ' --exclude=/api/* --include=/mobile/dist/ '
    elif app in [APP_TMS_APP]:
        cmdTplExt += ' --exclude=/api/* --include=/tms-applet/dist/ '
    else:
        cmdTplExt += ' --exclude=/api/* --include=/web/dist/ --include=/web/distv1/ '
     
elif onlyPubApi is True:
    cmdTplExt += ' --include=/api/ '
else:
    cmdTplExt += ' --include=/api/ --include=/web/dist/ --include=/web/distv1/ '

if app in [APP_TMS, APP_TMS_APP, APP_TMS_DESKTOP, APP_TMS_MOBILE]:
    cmdTplExt += ' --exclude=/mobile/* --exclude=/tms-applet/*  '

cmdTplExt += ' --exclude=/web/* --exclude=/log ' + CliTool.excludeFile() + localSrc

cmdTpl += cmdTplExt + ' www@%s::%s/'
sshCmdTpl += cmdTplExt + ' root@%s:/alidata/%s/'

syntax_check = False if (isIntra is True) or (isWebOnly is True) or is_php8_idc(idc=idc) is True else True

Ct = CliTool()
Ct.publish(ips, mode, cmdTpl, False, sshCmdTpl, php_syntax_check = syntax_check)


