#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Git 仓库监听器
监听多个 Git 仓库的指定分支，检测到新提交时触发部署
"""

import os
import sys
import time
import yaml
import subprocess
from datetime import datetime
from pathlib import Path

# 添加当前目录到 Python 路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

# 导入自定义模块
from scripts.log import Logger
from scripts import docker, maven, npm
from scripts.init import mysql, redis, nacos


class GitMonitor:
    """Git 仓库监听器"""

    def __init__(self, config_path='.devops/config.yaml'):
        """初始化监听器"""
        self.config_path = config_path
        self.config = None
        self.last_commits = {}
        self.global_branch = 'main'
        self.project_root = None
        self.runtime_path = None

        # 初始化
        self._print_startup_banner()
        self._load_config()
        self._init_paths()

    def _print_startup_banner(self):
        """打印启动横幅"""
        print("\n")
        Logger.separator()
        print("    RuoYi Cloud DevOps 自动化部署系统")
        Logger.separator()
        print(f"启动时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        Logger.separator()
        print("\n")

    def _load_config(self):
        """加载配置文件"""
        Logger.info(f"[步骤 1/3] 读取配置文件: {self.config_path}")
        try:
            with open(self.config_path, 'r', encoding='utf-8') as f:
                self.config = yaml.safe_load(f)

            self.global_branch = self.config.get('global_branch', 'main')

            # 初始化日志配置
            log_config = self.config.get('logging', {})
            log_file = log_config.get('file', '.devops/logs/devops.log')
            max_size = log_config.get('max_size', 10485760)
            Logger.init(log_file=log_file, max_size=max_size)

            Logger.info(f"✓ 配置加载成功 - 全局分支: {self.global_branch}")
            Logger.info(f"✓ 日志配置 - 文件: {log_file}, 最大大小: {max_size} 字节")
        except Exception as e:
            Logger.error(f"配置加载失败: {e}")
            sys.exit(1)

    def _init_paths(self):
        """初始化路径"""
        Logger.info("[步骤 2/3] 初始化路径")
        try:
            self.project_root = Path(__file__).parent.parent.resolve()
            runtime_path = self.config['main_repository']['runtime_path']

            if not Path(runtime_path).is_absolute():
                self.runtime_path = self.project_root / runtime_path
            else:
                self.runtime_path = Path(runtime_path)

            Logger.info(f"✓ 路径初始化成功")
            Logger.info(f"  项目根目录: {self.project_root}")
            Logger.info(f"  Runtime 目录: {self.runtime_path}")
        except Exception as e:
            Logger.error(f"路径初始化失败: {e}")
            sys.exit(1)

    def get_remote_commit(self, repo_url, branch):
        """获取远程仓库的最新提交 hash"""
        try:
            cmd = f"git ls-remote {repo_url} refs/heads/{branch}"
            result = subprocess.run(
                cmd, shell=True, capture_output=True, text=True, timeout=30
            )
            if result.returncode == 0 and result.stdout:
                return result.stdout.split()[0]
            return None
        except Exception as e:
            Logger.error(f"获取远程提交失败 {repo_url}: {e}")
            return None

    def check_repository(self, repo_config):
        """检查单个仓库是否有新提交"""
        repo_name = repo_config['name']
        repo_url = repo_config['url']

        current_commit = self.get_remote_commit(repo_url, self.global_branch)
        if not current_commit:
            return False

        last_commit = self.last_commits.get(repo_name)
        if last_commit is None:
            self.last_commits[repo_name] = current_commit
            Logger.info(f"初始化 {repo_name} 提交记录: {current_commit[:8]}")
            return False

        if current_commit != last_commit:
            Logger.info(f"检测到 {repo_name} 新提交: {last_commit[:8]} -> {current_commit[:8]}")
            self.last_commits[repo_name] = current_commit
            return True

        return False

    def update_main_repo(self):
        """更新主仓库和所有子模块"""
        repo_path = self.runtime_path / 'a-cloud-all'
        main_repo_url = self.config['main_repository']['url']

        Logger.separator()
        Logger.info("更新主仓库和子模块")
        Logger.separator()

        # 检查主仓库是否存在
        if not (repo_path / '.git').exists():
            Logger.info("主仓库不存在，开始克隆...")
            self.runtime_path.mkdir(parents=True, exist_ok=True)

            cmd = f"git clone --recurse-submodules {main_repo_url} a-cloud-all"
            result = subprocess.run(cmd, shell=True, cwd=self.runtime_path, capture_output=True, text=True)

            if result.returncode != 0:
                Logger.error("克隆主仓库失败")
                return False
            Logger.info("主仓库克隆成功")
        else:
            Logger.info("主仓库已存在，更新代码...")

            # 切换到主分支
            cmd = f"git checkout {self.global_branch}"
            subprocess.run(cmd, shell=True, cwd=repo_path, capture_output=True)

            # 拉取最新代码
            cmd = "git pull"
            result = subprocess.run(cmd, shell=True, cwd=repo_path, capture_output=True, text=True)
            if result.returncode != 0:
                Logger.error("拉取主仓库失败")
                return False

            # 更新所有子模块
            cmd = f"git submodule foreach 'git checkout {self.global_branch} && git pull'"
            result = subprocess.run(cmd, shell=True, cwd=repo_path, capture_output=True, text=True)
            if result.returncode != 0:
                Logger.error("更新子模块失败")
                return False

            Logger.info("主仓库和子模块更新成功")

        return True

    def init_infrastructure(self):
        """初始化基础设施（MySQL、Redis、Nacos）"""
        repo_path = self.runtime_path / 'a-cloud-all'

        # 检查是否已初始化
        mysql_flag = repo_path / '.devops' / '.deployed_mysql'
        redis_flag = repo_path / '.devops' / '.deployed_redis'
        nacos_flag = repo_path / '.devops' / '.deployed_nacos'

        # 初始化 MySQL
        if not mysql_flag.exists():
            Logger.info("初始化 MySQL...")
            # 从配置文件中获取 MySQL 的预部署命令
            infra_config = self.config.get('infrastructure', [])
            mysql_config = next((item for item in infra_config if item['name'] == 'ruoyi-mysql'), None)
            pre_deploy_commands = mysql_config.get('pre_deploy_commands', []) if mysql_config else []

            if mysql.init_mysql(repo_path, pre_deploy_commands):
                mysql_flag.parent.mkdir(parents=True, exist_ok=True)
                mysql_flag.touch()
                Logger.info("等待 MySQL 启动（30秒）...")
                time.sleep(30)
            else:
                return False

        # 初始化 Redis
        if not redis_flag.exists():
            Logger.info("初始化 Redis...")
            if redis.init_redis(repo_path):
                redis_flag.touch()
                Logger.info("等待 Redis 启动（10秒）...")
                time.sleep(10)
            else:
                return False

        # 初始化 Nacos
        if not nacos_flag.exists():
            Logger.info("初始化 Nacos...")
            if nacos.init_nacos(repo_path):
                nacos_flag.touch()
                Logger.info("等待 Nacos 启动（20秒）...")
                time.sleep(20)
            else:
                return False

        return True

    def deploy(self, repo_config):
        """执行部署流程"""
        repo_path = self.runtime_path / 'a-cloud-all'

        Logger.separator()
        Logger.info(f"开始部署: {repo_config['name']}")
        Logger.separator()

        try:
            # 1. 更新主仓库和子模块
            if not self.update_main_repo():
                return False

            # 2. 初始化基础设施
            if not self.init_infrastructure():
                return False

            # 3. 根据项目类型执行打包
            if repo_config['type'] == 'java':
                # Maven 打包
                work_dir = repo_path
                commands = ' && '.join(repo_config['build_commands'])
                source_path = repo_config['path'] + '/' + repo_config['artifact_path']
                target_dir = repo_path / repo_config['docker_path']

                if not maven.run_maven(work_dir, commands, source_path, target_dir):
                    return False

            elif repo_config['type'] == 'nodejs':
                # NPM 打包
                work_dir = repo_path / repo_config['path']
                commands = ' && '.join(repo_config['build_commands'])
                source_dir = repo_config['artifact_path']
                target_dir = repo_path / repo_config['docker_path']

                if not npm.run_npm(work_dir, commands, source_dir, target_dir):
                    return False

            # 4. Docker 部署
            compose_dir = repo_path / 'docker'
            service_name = repo_config['docker_service']

            if not docker.run_docker_compose(compose_dir, service_name):
                return False

            Logger.info(f"部署完成: {repo_config['name']}")
            return True

        except Exception as e:
            Logger.error(f"部署异常: {e}")
            return False

    def run_once(self):
        """执行一次检查"""
        Logger.info("[步骤 3/3] 开始监听分支变化")
        repos = self.config.get('repositories', [])

        for repo_config in repos:
            try:
                if self.check_repository(repo_config):
                    Logger.info(f"触发部署: {repo_config['name']}")
                    if self.deploy(repo_config):
                        Logger.info(f"✓ 部署成功: {repo_config['name']}")
                    else:
                        Logger.error(f"✗ 部署失败: {repo_config['name']}")
            except Exception as e:
                Logger.error(f"处理仓库异常 {repo_config['name']}: {e}")

    def run(self):
        """持续监听运行"""
        poll_interval = self.config['monitor']['poll_interval']
        Logger.info(f"开始持续监听，轮询间隔: {poll_interval} 秒")
        Logger.info("按 Ctrl+C 停止监听\n")

        try:
            while True:
                self.run_once()
                time.sleep(poll_interval)
        except KeyboardInterrupt:
            Logger.info("\n收到停止信号，退出监听")
        except Exception as e:
            Logger.error(f"监听异常: {e}")


def main():
    """主函数"""
    import argparse

    parser = argparse.ArgumentParser(description='Git 仓库监听器')
    parser.add_argument('--config', default='.devops/config.yaml', help='配置文件路径')
    parser.add_argument('--once', action='store_true', help='只执行一次检查')

    args = parser.parse_args()

    monitor = GitMonitor(args.config)

    if args.once:
        monitor.run_once()
    else:
        monitor.run()


if __name__ == '__main__':
    main()

