from loguru import logger
from typing import Dict, List, Optional
import os
import glob
from datetime import datetime, timedelta
import subprocess
import json
from influxdb_client import InfluxDBClient, Point
from influxdb_client.client.write_api import SYNCHRONOUS
from config.config import settings

class CoolingMonitor:
    def __init__(self):
        self.influx_client = InfluxDBClient(
            url=settings.INFLUXDB_URL,
            token=settings.INFLUXDB_TOKEN,
            org=settings.INFLUXDB_ORG
        )
        self.write_api = self.influx_client.write_api(write_options=SYNCHRONOUS)
        self.sensors = {}
        self._initialize_sensors()
        logger.info("냉각 시스템 모니터링 초기화 완료")

    def _initialize_sensors(self):
        """시스템의 온도 센서들을 초기화합니다."""
        try:
            # hwmon 디렉토리 찾기
            hwmon_paths = glob.glob('/sys/class/hwmon/hwmon*')
            
            for hwmon_path in hwmon_paths:
                try:
                    # 센서 이름 읽기
                    name_path = os.path.join(hwmon_path, 'name')
                    if not os.path.exists(name_path):
                        continue
                        
                    with open(name_path, 'r') as f:
                        name = f.read().strip()
                    
                    # 온도 센서 찾기
                    temp_inputs = glob.glob(os.path.join(hwmon_path, 'temp*_input'))
                    if temp_inputs:
                        logger.info(f"{name} 온도 센서 발견: {hwmon_path}")
                        self.sensors[name] = {
                            'path': hwmon_path,
                            'temp_inputs': temp_inputs,
                            'labels': self._get_sensor_labels(hwmon_path, len(temp_inputs))
                        }
                except Exception as e:
                    logger.warning(f"센서 {hwmon_path} 초기화 실패: {str(e)}")
                    continue
                    
        except Exception as e:
            logger.error(f"센서 초기화 중 오류 발생: {str(e)}")

    def _get_sensor_labels(self, hwmon_path: str, count: int) -> List[str]:
        """센서의 레이블을 가져옵니다."""
        labels = []
        for i in range(count):
            label_path = os.path.join(hwmon_path, f'temp{i+1}_label')
            if os.path.exists(label_path):
                try:
                    with open(label_path, 'r') as f:
                        labels.append(f.read().strip())
                except:
                    labels.append(f"temp{i+1}")
            else:
                labels.append(f"temp{i+1}")
        return labels

    def get_sensors_info(self):
        try:
            result = subprocess.run(['sensors', '-j'], capture_output=True, text=True)
            return json.loads(result.stdout)
        except Exception as e:
            logger.error(f"sensors 명령어 실행 실패: {str(e)}")
            return {"error": "sensors 명령어를 실행할 수 없습니다."}

    def get_thermal_info(self):
        thermal_info = {}
        thermal_path = '/sys/class/thermal'
        if os.path.exists(thermal_path):
            for zone in os.listdir(thermal_path):
                if zone.startswith('thermal_zone'):
                    temp_file = os.path.join(thermal_path, zone, 'temp')
                    if os.path.exists(temp_file):
                        with open(temp_file, 'r') as f:
                            temp = float(f.read().strip()) / 1000.0  # 밀리켈빈을 섭씨로 변환
                            thermal_info[zone] = temp
        return thermal_info

    def get_coolant_info(self):
        """수냉 시스템 정보 수집"""
        coolant_info = {}
        try:
            # 수냉 시스템 센서 정보 수집
            sensors_data = self.get_sensors_info()
            
            # 수냉 시스템 관련 센서 찾기
            for chip, data in sensors_data.items():
                if 'coolant' in chip.lower() or 'water' in chip.lower():
                    for sensor, values in data.items():
                        if 'temp' in sensor.lower():
                            coolant_info['temperature'] = float(values.get('temp1_input', 0))
                        elif 'flow' in sensor.lower():
                            coolant_info['flow_rate'] = float(values.get('flow1_input', 0))
                        elif 'level' in sensor.lower():
                            coolant_info['level'] = float(values.get('level1_input', 0))
            
            # 수냉 시스템 상태 확인
            if 'temperature' in coolant_info:
                if coolant_info['temperature'] > settings.COOLANT_TEMP_THRESHOLD:
                    logger.warning(f"냉각수 온도가 임계값을 초과: {coolant_info['temperature']}°C")
            
            if 'level' in coolant_info:
                if coolant_info['level'] < settings.MIN_COOLANT_LEVEL:
                    logger.warning(f"냉각수 레벨이 낮음: {coolant_info['level']*100:.1f}%")
            
            return coolant_info
        except Exception as e:
            logger.error(f"냉각 시스템 정보 수집 실패: {str(e)}")
            return {}

    def save_to_influxdb(self, thermal_data, coolant_data):
        try:
            # 열 정보 저장
            for zone, temp in thermal_data.items():
                point = Point("thermal_metrics") \
                    .tag("zone", zone) \
                    .field("temperature", temp) \
                    .time(datetime.utcnow())
                self.write_api.write(bucket=settings.INFLUXDB_BUCKET, record=point)

            # 냉각수 정보 저장
            if coolant_data:
                point = Point("coolant_metrics") \
                    .field("temperature", coolant_data.get('temperature', 0)) \
                    .field("flow_rate", coolant_data.get('flow_rate', 0)) \
                    .field("level", coolant_data.get('level', 0)) \
                    .time(datetime.utcnow())
                self.write_api.write(bucket=settings.INFLUXDB_BUCKET, record=point)

        except Exception as e:
            logger.error(f"InfluxDB 저장 실패: {str(e)}")

    def check_cooling_status(self):
        alerts = []
        
        # 열 정보 수집
        thermal_data = self.get_thermal_info()
        
        # 냉각수 정보 수집
        coolant_data = self.get_coolant_info()
        
        # InfluxDB에 데이터 저장
        self.save_to_influxdb(thermal_data, coolant_data)
        
        # 알림 생성
        if coolant_data:
            if coolant_data.get('temperature', 0) > settings.COOLANT_TEMP_THRESHOLD:
                alerts.append({
                    'type': 'coolant_temperature',
                    'message': f"냉각수 온도가 임계값을 초과했습니다: {coolant_data['temperature']}°C",
                    'value': coolant_data['temperature'],
                    'threshold': settings.COOLANT_TEMP_THRESHOLD
                })
            
            if coolant_data.get('level', 1) < settings.MIN_COOLANT_LEVEL:
                alerts.append({
                    'type': 'coolant_level',
                    'message': f"냉각수 레벨이 낮습니다: {coolant_data['level']*100:.1f}%",
                    'value': coolant_data['level'],
                    'threshold': settings.MIN_COOLANT_LEVEL
                })
        
        return alerts

    def __del__(self):
        if hasattr(self, 'influx_client'):
            self.influx_client.close()

    def get_metrics_history(self, start_time: datetime, end_time: datetime) -> List[Dict]:
        """지정된 기간 동안의 메트릭 히스토리를 반환합니다."""
        try:
            # 실제로는 InfluxDB나 다른 시계열 데이터베이스에서 데이터를 가져와야 하지만,
            # 현재는 더미 데이터를 생성하여 반환합니다.
            metrics = []
            current_time = start_time
            
            while current_time <= end_time:
                temp_info = self.get_temperature_info()
                for sensor_name, temp in temp_info.items():
                    metrics.append({
                        '_measurement': 'cooling_metrics',
                        '_time': current_time.isoformat(),
                        '_value': temp,
                        'sensor': sensor_name
                    })
                current_time += timedelta(hours=1)
            
            return metrics
        except Exception as e:
            logger.error(f"메트릭 히스토리 조회 중 오류 발생: {str(e)}")
            return [] 