"""
持仓定时分析调度器模块

提供定时任务调度功能，在设定时间自动执行持仓批量分析
"""

import schedule
import threading
import time
from datetime import datetime
from typing import Optional, Callable
import traceback

from portfolio_manager import portfolio_manager
from notification_service import NotificationService


class PortfolioScheduler:
    """持仓分析定时调度器"""
    
    def __init__(self):
        """初始化调度器"""
        self.schedule_times = ["09:30"]  # 支持多个定时时间点
        self.analysis_mode = "sequential"  # 默认顺序分析
        self._is_running = False  # 使用私有属性
        self.thread = None
        self.last_run_time = None
        self.next_run_time = None
        self.auto_monitor_sync = True  # 默认启用自动监测同步
        self.notification_enabled = True  # 默认启用通知
        self.selected_agents = None  # None表示全部分析师
        self.notification_service = NotificationService()
        self.max_workers = 3  # 并行模式的线程数
    
    # 兼容旧代码的属性
    @property
    def schedule_time(self) -> str:
        """获取第一个定时时间（向后兼容）"""
        return self.schedule_times[0] if self.schedule_times else "09:30"
    
    def is_running(self) -> bool:
        """
        检查调度器是否正在运行
        
        Returns:
            bool: True表示运行中，False表示已停止
        """
        return self._is_running
    
    def set_schedule_time(self, time_str: str):
        """
        设置定时分析时间（向后兼容，设置为单个时间）
        
        Args:
            time_str: 时间字符串，格式"HH:MM"（如"08:00"）
        """
        try:
            # 验证时间格式
            datetime.strptime(time_str, "%H:%M")
            self.schedule_times = [time_str]
            print(f"[OK] 设置定时分析时间: {time_str}")
            
            # 如果调度器正在运行，重新调度
            if self._is_running:
                self._reschedule()
                
        except ValueError:
            print(f"[ERROR] 无效的时间格式: {time_str}，应为 HH:MM")
    
    def add_schedule_time(self, time_str: str) -> bool:
        """
        添加一个定时分析时间点
        
        Args:
            time_str: 时间字符串，格式"HH:MM"
            
        Returns:
            是否添加成功
        """
        try:
            # 验证时间格式
            datetime.strptime(time_str, "%H:%M")
            
            # 检查是否已存在
            if time_str in self.schedule_times:
                print(f"[WARN] 定时时间 {time_str} 已存在")
                return False
            
            self.schedule_times.append(time_str)
            self.schedule_times.sort()  # 保持时间顺序
            print(f"[OK] 添加定时时间: {time_str}")
            
            # 如果调度器正在运行，重新调度
            if self._is_running:
                self._reschedule()
            
            return True
            
        except ValueError:
            print(f"[ERROR] 无效的时间格式: {time_str}，应为 HH:MM")
            return False
    
    def remove_schedule_time(self, time_str: str) -> bool:
        """
        删除一个定时分析时间点
        
        Args:
            time_str: 时间字符串
            
        Returns:
            是否删除成功
        """
        if time_str in self.schedule_times:
            self.schedule_times.remove(time_str)
            print(f"[OK] 删除定时时间: {time_str}")
            
            # 如果调度器正在运行，重新调度
            if self._is_running:
                self._reschedule()
            
            return True
        else:
            print(f"[WARN] 定时时间 {time_str} 不存在")
            return False
    
    def get_schedule_times(self) -> list:
        """
        获取所有定时分析时间点
        
        Returns:
            时间列表
        """
        return self.schedule_times.copy()
    
    def set_schedule_times(self, times: list):
        """
        批量设置定时分析时间点
        
        Args:
            times: 时间字符串列表
        """
        valid_times = []
        for time_str in times:
            try:
                datetime.strptime(time_str, "%H:%M")
                valid_times.append(time_str)
            except ValueError:
                print(f"[WARN] 跳过无效时间: {time_str}")
        
        if valid_times:
            self.schedule_times = sorted(valid_times)
            print(f"[OK] 设置定时时间: {', '.join(self.schedule_times)}")
            
            # 如果调度器正在运行，重新调度
            if self._is_running:
                self._reschedule()
        else:
            print(f"[ERROR] 没有有效的时间配置")
    
    def set_analysis_mode(self, mode: str):
        """
        设置分析模式
        
        Args:
            mode: "sequential" 或 "parallel"
        """
        if mode in ["sequential", "parallel"]:
            self.analysis_mode = mode
            print(f"[OK] 设置分析模式: {mode}")
        else:
            print(f"[ERROR] 无效的分析模式: {mode}")
    
    def set_auto_monitor_sync(self, enabled: bool):
        """设置是否启用自动监测同步"""
        self.auto_monitor_sync = enabled
        print(f"[OK] 自动监测同步: {'启用' if enabled else '禁用'}")
    
    def set_notification_enabled(self, enabled: bool):
        """设置是否启用通知"""
        self.notification_enabled = enabled
        print(f"[OK] 通知推送: {'启用' if enabled else '禁用'}")
    
    def set_selected_agents(self, agents: Optional[list]):
        """设置参与分析的AI分析师"""
        self.selected_agents = agents
        if agents:
            print(f"[OK] 选择分析师: {', '.join(agents)}")
        else:
            print("[OK] 选择分析师: 全部")
    
    def _scheduled_job(self):
        """定时任务执行的作业"""
        print("\n" + "="*60)
        print(f"定时分析开始: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print("="*60 + "\n")
        
        try:
            # 1. 执行批量分析
            print("[1/4] 执行持仓批量分析...")
            analysis_results = portfolio_manager.batch_analyze_portfolio(
                mode=self.analysis_mode,
                max_workers=self.max_workers,
                selected_agents=self.selected_agents
            )
            
            if not analysis_results.get("success"):
                error_msg = analysis_results.get("error", "未知错误")
                print(f"[ERROR] 批量分析失败: {error_msg}")
                
                # 发送错误通知
                if self.notification_enabled:
                    self._send_error_notification(error_msg)
                
                self.last_run_time = datetime.now()
                return
            
            # 2. 保存分析结果
            print("\n[2/4] 保存分析结果...")
            saved_ids = portfolio_manager.save_analysis_results(analysis_results)
            print(f"[OK] 保存 {len(saved_ids)} 条分析记录")
            
            # 3. 自动监测同步
            sync_result = None
            if self.auto_monitor_sync:
                print("\n[3/4] 自动同步到监测列表...")
                sync_result = self._sync_to_monitor(analysis_results)
            else:
                print("\n[3/4] 跳过监测同步（已禁用）")
            
            # 4. 发送通知
            if self.notification_enabled:
                print("\n[4/4] 发送通知...")
                self._send_notification(analysis_results, sync_result)
            else:
                print("\n[4/4] 跳过通知发送（已禁用）")
            
            # 更新运行时间
            self.last_run_time = datetime.now()
            
            print("\n" + "="*60)
            print(f"定时分析完成: {self.last_run_time.strftime('%Y-%m-%d %H:%M:%S')}")
            print("="*60 + "\n")
            
        except Exception as e:
            print(f"\n[ERROR] 定时任务执行异常: {str(e)}")
            traceback.print_exc()
            
            # 发送错误通知
            if self.notification_enabled:
                self._send_error_notification(str(e))
            
            self.last_run_time = datetime.now()
    
    def _sync_to_monitor(self, analysis_results: dict) -> dict:
        """
        同步分析结果到监测列表
        
        Args:
            analysis_results: 批量分析结果
            
        Returns:
            同步结果统计
        """
        try:
            from monitor_db import monitor_db
            
            # 准备批量监测数据
            monitors_data = []
            failed_count = 0
            
            for item in analysis_results.get("results", []):
                code = item.get("code")
                result = item.get("result", {})
                
                # 检查分析是否成功
                if not result.get("success"):
                    continue
                
                final_decision = result.get("final_decision", {})
                stock_info = result.get("stock_info", {})
                
                # 检查是否启用自动监测
                stock = portfolio_manager.db.get_stock_by_code(code)
                if not stock or not stock.get("auto_monitor"):
                    continue
                
                # 从final_decision中提取数据（使用正确的字段名）
                rating = final_decision.get("rating", "持有")
                entry_range = final_decision.get("entry_range", "")
                take_profit_str = final_decision.get("take_profit", "")
                stop_loss_str = final_decision.get("stop_loss", "")
                
                # 解析进场区间（格式如"10.5-12.3"）
                entry_min, entry_max = None, None
                if entry_range and isinstance(entry_range, str) and "-" in entry_range:
                    try:
                        parts = entry_range.split("-")
                        entry_min = float(parts[0].strip())
                        entry_max = float(parts[1].strip())
                    except:
                        pass
                
                # 解析止盈止损（提取数字）
                import re
                take_profit, stop_loss = None, None
                if take_profit_str:
                    try:
                        numbers = re.findall(r'\d+\.?\d*', str(take_profit_str))
                        if numbers:
                            take_profit = float(numbers[0])
                    except:
                        pass
                
                if stop_loss_str:
                    try:
                        numbers = re.findall(r'\d+\.?\d*', str(stop_loss_str))
                        if numbers:
                            stop_loss = float(numbers[0])
                    except:
                        pass
                
                # 检查参数有效性
                if not all([entry_min, entry_max, take_profit, stop_loss]):
                    print(f"[WARN] {code} 参数不完整，跳过同步")
                    failed_count += 1
                    continue
                
                # 构建监测数据
                monitor_data = {
                    "code": code,
                    "name": stock_info.get("name", stock.get("name", code)),
                    "rating": rating,
                    "entry_min": entry_min,
                    "entry_max": entry_max,
                    "take_profit": take_profit,
                    "stop_loss": stop_loss,
                    "check_interval": 60,
                    "notification_enabled": True
                }
                
                monitors_data.append(monitor_data)
            
            # 使用批量API同步
            if monitors_data:
                result = monitor_db.batch_add_or_update_monitors(monitors_data)
                return result
            else:
                print("[WARN] 没有需要同步的监测数据")
                return {"added": 0, "updated": 0, "failed": 0, "total": 0}
            
        except Exception as e:
            print(f"[ERROR] 监测同步异常: {str(e)}")
            import traceback
            traceback.print_exc()
            return {"added": 0, "updated": 0, "failed": 0, "total": 0}
    
    def _send_notification(self, analysis_results: dict, sync_result: Optional[dict]):
        """
        发送分析完成通知（使用新的notification_service方法）
        
        Args:
            analysis_results: 批量分析结果
            sync_result: 监测同步结果
        """
        try:
            from notification_service import notification_service
            
            # 使用新的专用通知方法
            success = notification_service.send_portfolio_analysis_notification(
                analysis_results, sync_result
            )
            
            if success:
                print("[OK] 持仓分析通知发送成功")
            else:
                print("[WARN] 持仓分析通知发送失败（可能未配置通知服务）")
            
        except Exception as e:
            print(f"[ERROR] 发送通知失败: {str(e)}")
            import traceback
            traceback.print_exc()
    
    def _send_error_notification(self, error_msg: str):
        """发送错误通知"""
        try:
            content = f"""
持仓定时分析执行失败

错误信息：
{error_msg}

时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}

请检查系统日志或手动运行分析。
"""
            
            if self.notification_service.email_enabled:
                self.notification_service.send_email("【持仓定时分析】执行失败", content)
            
            if self.notification_service.webhook_enabled:
                self.notification_service.send_webhook("【持仓定时分析】执行失败", content)
                
        except Exception as e:
            print(f"[ERROR] 发送错误通知失败: {str(e)}")
    
    def _generate_notification_content(self, analysis_results: dict, 
                                      sync_result: Optional[dict]) -> str:
        """
        生成通知内容
        
        Args:
            analysis_results: 批量分析结果
            sync_result: 监测同步结果
            
        Returns:
            通知内容文本
        """
        total = analysis_results.get("total", 0)
        succeeded = analysis_results.get("succeeded", 0)
        failed = analysis_results.get("failed", 0)
        mode = analysis_results.get("mode", "sequential")
        elapsed_time = analysis_results.get("elapsed_time", 0)
        
        # 统计评级分布
        rating_stats = {"买入": 0, "持有": 0, "卖出": 0}
        rating_changes = []
        
        for item in analysis_results.get("results", []):
            code = item.get("code")
            result = item.get("result", {})
            final_decision = result.get("final_decision", {})
            rating = final_decision.get("investment_rating", "持有")
            
            rating_stats[rating] = rating_stats.get(rating, 0) + 1
            
            # 检查评级变化
            stock = portfolio_manager.db.get_stock_by_code(code)
            if stock:
                history = portfolio_manager.db.get_analysis_history(stock['id'], limit=2)
                if len(history) >= 2:
                    old_rating = history[1]['rating']
                    new_rating = history[0]['rating']
                    if old_rating != new_rating:
                        stock_info = result.get("stock_info", {})
                        name = stock_info.get("name", stock.get("name", code))
                        rating_changes.append(f"{code} {name}: {old_rating} → {new_rating}")
        
        # 构建通知内容
        content = f"""
持仓定时分析报告 - {datetime.now().strftime('%Y-%m-%d %H:%M')}

📊 分析完成：{total}只持仓股票
✅ 成功：{succeeded}只
❌ 失败：{failed}只
⏱ 耗时：{elapsed_time:.1f}秒
🔄 模式：{'顺序分析' if mode == 'sequential' else '并行分析'}

📈 投资评级分布：
• 买入：{rating_stats.get('买入', 0)}只
• 持有：{rating_stats.get('持有', 0)}只
• 卖出：{rating_stats.get('卖出', 0)}只
"""
        
        # 添加评级变化
        if rating_changes:
            content += "\n🔔 评级变化：\n"
            for change in rating_changes[:5]:  # 最多显示5个
                content += f"• {change}\n"
        
        # 添加监测同步结果
        if sync_result:
            content += f"""
🎯 监测同步：
• 新增：{sync_result.get('added', 0)}只
• 更新：{sync_result.get('updated', 0)}只
• 失败：{sync_result.get('failed', 0)}只
"""
        
        # 添加失败股票
        if failed > 0:
            failed_stocks = analysis_results.get("failed_stocks", [])
            content += "\n⚠️ 失败股票：\n"
            for stock in failed_stocks[:3]:  # 最多显示3个
                content += f"• {stock.get('code')}: {stock.get('error')}\n"
        
        content += "\n详细报告请登录系统查看。"
        
        return content
    
    def _reschedule(self):
        """重新调度任务（支持多个时间点）"""
        # 只清除持仓定时分析的任务，不影响其他模块
        jobs_to_remove = [job for job in schedule.jobs if not any(tag in ['sector_strategy', 'monitor'] for tag in job.tags)]
        for job in jobs_to_remove:
            schedule.cancel_job(job)
        
        for time_str in self.schedule_times:
            job = schedule.every().day.at(time_str).do(self._scheduled_job)
            job.tag('portfolio_analysis')
        self._update_next_run_time()
        print(f"[OK] 重新调度任务: 每天 {', '.join(self.schedule_times)}")
    
    def _update_next_run_time(self):
        """更新下次运行时间"""
        jobs = schedule.jobs
        if jobs:
            self.next_run_time = jobs[0].next_run
        else:
            self.next_run_time = None
    
    def _run_schedule_loop(self):
        """调度循环（在后台线程中运行）"""
        print("[OK] 定时调度器线程启动")
        
        while self._is_running:
            schedule.run_pending()
            self._update_next_run_time()
            time.sleep(1)
        
        print("[OK] 定时调度器线程停止")
    
    def start(self) -> bool:
        """
        启动定时任务
        
        Returns:
            是否启动成功
        """
        if self._is_running:
            print("[WARN] 定时任务已在运行中")
            return False
        
        # 检查持仓数量
        stock_count = portfolio_manager.get_stock_count()
        if stock_count == 0:
            print("[ERROR] 没有持仓股票，无法启动定时任务")
            return False
        
        # 检查时间配置
        if not self.schedule_times:
            print("[ERROR] 没有配置定时时间")
            return False
        
        # 调度任务（为每个时间点创建任务）
        # 只清除持仓定时分析的任务，不影响智策和监测任务
        jobs_to_remove = [job for job in schedule.jobs if 'portfolio_analysis' in job.tags]
        for job in jobs_to_remove:
            schedule.cancel_job(job)
        print(f"[OK] 清除了 {len(jobs_to_remove)} 个旧的持仓任务")
        
        for time_str in self.schedule_times:
            job = schedule.every().day.at(time_str).do(self._scheduled_job)
            job.tag('portfolio_analysis')
            print(f"[OK] 添加调度任务: 每天 {time_str}")
        
        self._update_next_run_time()
        
        # 启动后台线程
        self._is_running = True
        self.thread = threading.Thread(target=self._run_schedule_loop, daemon=True)
        self.thread.start()
        
        print(f"\n[OK] 定时任务已启动")
        print(f"    调度时间: {', '.join(self.schedule_times)}")
        print(f"    分析模式: {self.analysis_mode}")
        print(f"    持仓数量: {stock_count}只")
        if self.next_run_time:
            print(f"    下次运行: {self.next_run_time.strftime('%Y-%m-%d %H:%M:%S')}")
        
        return True
    
    def stop(self) -> bool:
        """
        停止定时任务
        
        Returns:
            是否停止成功
        """
        if not self._is_running:
            print("[WARN] 定时任务未运行")
            return False
        
        self._is_running = False
        
        # 只清除持仓定时分析的任务，不影响其他模块（智策、监测）
        try:
            jobs_to_remove = [job for job in schedule.jobs if 'portfolio_analysis' in job.tags]
            for job in jobs_to_remove:
                schedule.cancel_job(job)
            print(f"[OK] 清除了 {len(jobs_to_remove)} 个持仓任务")
        except Exception as e:
            print(f"[WARN] 清除任务时出错: {e}")
        
        # 等待线程结束（最多等待2秒）
        if self.thread and self.thread.is_alive():
            self.thread.join(timeout=2)
        
        self.thread = None
        self.next_run_time = None
        
        print("[OK] 定时任务已停止")
        return True
    
    def run_once(self) -> bool:
        """
        立即执行一次分析（不影响定时计划）
        
        Returns:
            是否执行成功
        """
        # 检查持仓数量
        stock_count = portfolio_manager.get_stock_count()
        if stock_count == 0:
            print("[ERROR] 没有持仓股票")
            return False
        
        print("[OK] 立即执行持仓分析...")
        self._scheduled_job()
        return True
    
    def get_status(self) -> dict:
        """
        获取定时任务状态
        
        Returns:
            状态字典
        """
        return {
            "is_running": self._is_running,
            "schedule_time": self.schedule_time,
            "analysis_mode": self.analysis_mode,
            "auto_monitor_sync": self.auto_monitor_sync,
            "notification_enabled": self.notification_enabled,
            "last_run_time": self.last_run_time.strftime("%Y-%m-%d %H:%M:%S") if self.last_run_time else None,
            "next_run_time": self.next_run_time.strftime("%Y-%m-%d %H:%M:%S") if self.next_run_time else None,
            "portfolio_count": portfolio_manager.get_stock_count()
        }
    
    def get_next_run_time(self) -> Optional[str]:
        """
        获取下次运行时间
        
        Returns:
            下次运行时间字符串，格式"HH:MM"，如果未设置则返回None
        """
        if self.next_run_time:
            return self.next_run_time.strftime("%H:%M")
        return None
    
    def update_config(self, schedule_time: str = None, analysis_mode: str = None,
                     max_workers: int = None, auto_sync_monitor: bool = None,
                     send_notification: bool = None):
        """
        更新调度器配置
        
        Args:
            schedule_time: 定时分析时间（格式"HH:MM"，可选，用于向后兼容）
            analysis_mode: 分析模式（"sequential"或"parallel"）
            max_workers: 并行线程数（仅在parallel模式下有效）
            auto_sync_monitor: 是否自动同步到监测
            send_notification: 是否发送通知
        """
        if schedule_time is not None:
            self.set_schedule_time(schedule_time)
        
        if analysis_mode is not None:
            self.set_analysis_mode(analysis_mode)
        
        if max_workers is not None:
            self.max_workers = max_workers
            print(f"[OK] 设置并行线程数: {max_workers}")
        
        if auto_sync_monitor is not None:
            self.set_auto_monitor_sync(auto_sync_monitor)
        
        if send_notification is not None:
            self.set_notification_enabled(send_notification)
        
        print("[OK] 配置已更新")
    
    def start_scheduler(self) -> bool:
        """
        启动调度器（UI友好方法名）
        
        Returns:
            是否启动成功
        """
        return self.start()
    
    def stop_scheduler(self) -> bool:
        """
        停止调度器（UI友好方法名）
        
        Returns:
            是否停止成功
        """
        return self.stop()
    
    def run_analysis_now(self) -> bool:
        """
        立即执行一次分析（UI友好方法名）
        
        Returns:
            是否执行成功
        """
        return self.run_once()


# 创建全局实例
portfolio_scheduler = PortfolioScheduler()


if __name__ == "__main__":
    # 测试代码
    print("="*60)
    print("持仓定时调度器测试")
    print("="*60)
    
    scheduler = PortfolioScheduler()
    
    # 设置配置
    scheduler.set_schedule_time("09:00")
    scheduler.set_analysis_mode("sequential")
    scheduler.set_auto_monitor_sync(True)
    scheduler.set_notification_enabled(False)  # 测试时禁用通知
    
    # 获取状态
    status = scheduler.get_status()
    print("\n调度器状态:")
    for key, value in status.items():
        print(f"  {key}: {value}")
    
    print("\n[OK] 调度器测试完成")

