"""
Dell iDRAC REDFISH API 클라이언트
Dell 서버 펌웨어 조회 최적화 버전
"""

import requests
import json
import time
from urllib3.exceptions import InsecureRequestWarning
from requests.auth import HTTPBasicAuth

# SSL 경고 비활성화
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)


class DellRedfishClient:
    def __init__(self, idrac_ip, username, password):
        """
        Dell iDRAC REDFISH 클라이언트 초기화
        
        Args:
            idrac_ip: iDRAC IP 주소
            username: iDRAC 사용자명
            password: iDRAC 비밀번호
        """
        self.idrac_ip = idrac_ip
        self.base_url = f"https://{idrac_ip}"
        self.auth = HTTPBasicAuth(username, password)
        self.headers = {'Content-Type': 'application/json'}
        self.session = requests.Session()
        self.session.verify = False
        self.session.auth = self.auth
        
    def check_connection(self):
        """iDRAC 연결 확인"""
        try:
            url = f"{self.base_url}/redfish/v1"
            response = self.session.get(url, timeout=10)
            return response.status_code == 200
        except Exception as e:
            print(f"연결 오류: {str(e)}")
            return False
    
    def get_firmware_inventory(self):
        """
        현재 설치된 펌웨어 목록 조회 (Dell 최적화)
        주요 컴포넌트만 필터링하여 반환
        """
        try:
            url = f"{self.base_url}/redfish/v1/UpdateService/FirmwareInventory"
            response = self.session.get(url, timeout=30)
            
            if response.status_code != 200:
                print(f"펌웨어 목록 조회 실패: {response.status_code}")
                return []
            
            data = response.json()
            inventory = []
            
            # 주요 컴포넌트 타입 (Dell 서버)
            important_components = [
                'BIOS', 'iDRAC', 'CPLD', 'Diagnostics',
                'Driver', 'Firmware', 'USC', 'NIC',
                'RAID', 'PERC', 'Storage', 'Backplane',
                'HBA', 'Network', 'Intel', 'Broadcom',
                'Mellanox', 'Emulex', 'QLogic'
            ]
            
            print(f"전체 펌웨어 항목 수: {len(data.get('Members', []))}")
            
            # 각 펌웨어 항목 조회
            for idx, member in enumerate(data.get('Members', [])):
                try:
                    fw_url = f"{self.base_url}{member.get('@odata.id', '')}"
                    fw_response = self.session.get(fw_url, timeout=10)
                    
                    if fw_response.status_code == 200:
                        fw_data = fw_response.json()
                        
                        # 이름과 버전 추출 (여러 필드 시도)
                        name = (fw_data.get('Name') or 
                               fw_data.get('SoftwareId') or 
                               fw_data.get('Id') or 
                               'Unknown')
                        
                        version = (fw_data.get('Version') or 
                                  fw_data.get('VersionString') or 
                                  'Unknown')
                        
                        # 컴포넌트 타입 확인
                        component_type = fw_data.get('ComponentType', '')
                        
                        # 중요 컴포넌트만 필터링
                        is_important = any(comp.lower() in name.lower() 
                                          for comp in important_components)
                        
                        if is_important or component_type:
                            item = {
                                'Name': name,
                                'Version': version,
                                'ComponentType': component_type,
                                'Updateable': fw_data.get('Updateable', False),
                                'Status': fw_data.get('Status', {}).get('Health', 'OK'),
                                'Id': fw_data.get('Id', ''),
                                'Description': fw_data.get('Description', ''),
                                'ReleaseDate': fw_data.get('ReleaseDate', '')
                            }
                            
                            inventory.append(item)
                            
                            # 진행 상황 출력
                            if (idx + 1) % 10 == 0:
                                print(f"조회 중... {idx + 1}/{len(data.get('Members', []))}")
                    
                except Exception as e:
                    print(f"펌웨어 항목 조회 오류 ({idx}): {str(e)}")
                    continue
            
            # 이름순 정렬
            inventory.sort(key=lambda x: x['Name'])
            
            print(f"중요 펌웨어 항목 수: {len(inventory)}")
            
            # 주요 컴포넌트 요약
            bios = next((x for x in inventory if 'BIOS' in x['Name']), None)
            idrac = next((x for x in inventory if 'iDRAC' in x['Name']), None)
            
            if bios:
                print(f"✓ BIOS 버전: {bios['Version']}")
            if idrac:
                print(f"✓ iDRAC 버전: {idrac['Version']}")
            
            return inventory
            
        except Exception as e:
            print(f"펌웨어 목록 조회 오류: {str(e)}")
            import traceback
            traceback.print_exc()
            return []
    
    def get_firmware_summary(self):
        """
        주요 펌웨어 버전만 간단히 조회
        BIOS, iDRAC, PERC 등 핵심 컴포넌트만
        """
        try:
            inventory = self.get_firmware_inventory()
            
            summary = {
                'BIOS': 'N/A',
                'iDRAC': 'N/A',
                'PERC': 'N/A',
                'NIC': 'N/A',
                'CPLD': 'N/A'
            }
            
            for item in inventory:
                name = item['Name']
                version = item['Version']
                
                if 'BIOS' in name:
                    summary['BIOS'] = version
                elif 'iDRAC' in name or 'Integrated Dell Remote Access' in name:
                    summary['iDRAC'] = version
                elif 'PERC' in name or 'RAID' in name:
                    if summary['PERC'] == 'N/A':
                        summary['PERC'] = version
                elif 'NIC' in name or 'Network' in name:
                    if summary['NIC'] == 'N/A':
                        summary['NIC'] = version
                elif 'CPLD' in name:
                    summary['CPLD'] = version
            
            return summary
            
        except Exception as e:
            print(f"펌웨어 요약 조회 오류: {str(e)}")
            return {}
    
    def upload_firmware_staged(self, dup_file_path):
        """
        DUP 파일을 iDRAC에 업로드 (스테이징만, 재부팅 시 적용)
        
        Args:
            dup_file_path: 업로드할 DUP 파일 경로
            
        Returns:
            dict: {'success': bool, 'job_id': str, 'message': str}
        """
        import os
        if not os.path.exists(dup_file_path):
            return {
                'success': False,
                'job_id': None,
                'message': f'파일을 찾을 수 없습니다: {dup_file_path}'
            }
        
        file_name = os.path.basename(dup_file_path)
        file_size = os.path.getsize(dup_file_path)
        
        print(f"파일 업로드: {file_name} ({file_size / (1024*1024):.2f} MB)")
        
        try:
            url = f"{self.base_url}/redfish/v1/UpdateService/MultipartUpload"
            
            # Multipart 업로드 준비
            with open(dup_file_path, 'rb') as f:
                files = {
                    'file': (file_name, f, 'application/octet-stream')
                }
                
                # targets=INSTALLED 파라미터로 스테이징 모드 설정
                data = {
                    '@Redfish.OperationApplyTime': 'OnReset',
                    'Targets': []
                }
                
                multipart_data = {
                    'UpdateParameters': (None, json.dumps(data), 'application/json')
                }
                multipart_data.update(files)
                
                print("업로드 시작...")
                response = self.session.post(
                    url,
                    files=multipart_data,
                    auth=self.auth,
                    verify=False,
                    timeout=600  # 10분 타임아웃
                )
                
                print(f"응답 코드: {response.status_code}")
                
                if response.status_code in [200, 201, 202]:
                    response_data = response.json()
                    
                    # Task 또는 Job ID 추출
                    task_id = None
                    if '@odata.id' in response_data:
                        task_id = response_data['@odata.id'].split('/')[-1]
                    elif 'Id' in response_data:
                        task_id = response_data['Id']
                    
                    # Location 헤더에서 Job ID 추출
                    location = response.headers.get('Location', '')
                    if location and not task_id:
                        task_id = location.split('/')[-1]
                    
                    print(f"업로드 완료! Task/Job ID: {task_id}")
                    
                    return {
                        'success': True,
                        'job_id': task_id or 'STAGED',
                        'message': '업로드 완료 (재부팅 시 적용)'
                    }
                else:
                    error_msg = response.text
                    print(f"업로드 실패: {error_msg}")
                    return {
                        'success': False,
                        'job_id': None,
                        'message': f'업로드 실패 (코드: {response.status_code})'
                    }
                    
        except Exception as e:
            print(f"업로드 오류: {str(e)}")
            import traceback
            traceback.print_exc()
            return {
                'success': False,
                'job_id': None,
                'message': f'업로드 오류: {str(e)}'
            }
    
    def get_job_queue(self):
        """Job Queue 조회"""
        try:
            url = f"{self.base_url}/redfish/v1/Managers/iDRAC.Embedded.1/Jobs"
            response = self.session.get(url, timeout=30)
            
            if response.status_code == 200:
                data = response.json()
                jobs = []
                
                for member in data.get('Members', [])[:20]:  # 최근 20개
                    job_url = f"{self.base_url}{member['@odata.id']}"
                    job_response = self.session.get(job_url, timeout=10)
                    
                    if job_response.status_code == 200:
                        job_data = job_response.json()
                        jobs.append({
                            'Id': job_data.get('Id', ''),
                            'Name': job_data.get('Name', ''),
                            'JobState': job_data.get('JobState', 'Unknown'),
                            'PercentComplete': job_data.get('PercentComplete', 0),
                            'Message': job_data.get('Message', ''),
                            'StartTime': job_data.get('StartTime', ''),
                            'EndTime': job_data.get('EndTime', '')
                        })
                
                return jobs
            else:
                return []
                
        except Exception as e:
            print(f"Job Queue 조회 오류: {str(e)}")
            return []
    
    def get_job_status(self, job_id):
        """특정 Job 상태 조회"""
        try:
            url = f"{self.base_url}/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/{job_id}"
            response = self.session.get(url, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                return {
                    'Id': data.get('Id', ''),
                    'Name': data.get('Name', ''),
                    'JobState': data.get('JobState', 'Unknown'),
                    'PercentComplete': data.get('PercentComplete', 0),
                    'Message': data.get('Message', ''),
                    'StartTime': data.get('StartTime', ''),
                    'EndTime': data.get('EndTime', '')
                }
            else:
                return None
                
        except Exception as e:
            print(f"Job 상태 조회 오류: {str(e)}")
            return None
    
    def delete_job(self, job_id):
        """Job 삭제"""
        try:
            url = f"{self.base_url}/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/{job_id}"
            response = self.session.delete(url, timeout=10)
            return response.status_code in [200, 204]
        except Exception as e:
            print(f"Job 삭제 오류: {str(e)}")
            return False
    
    def get_power_status(self):
        """서버 전원 상태 조회"""
        try:
            url = f"{self.base_url}/redfish/v1/Systems/System.Embedded.1"
            response = self.session.get(url, timeout=10)
            
            if response.status_code == 200:
                data = response.json()
                return {
                    'PowerState': data.get('PowerState', 'Unknown'),
                    'Status': data.get('Status', {}).get('Health', 'Unknown')
                }
            else:
                return {'PowerState': 'Unknown', 'Status': 'Unknown'}
                
        except Exception as e:
            print(f"전원 상태 조회 오류: {str(e)}")
            return {'PowerState': 'Unknown', 'Status': 'Unknown'}
    
    def reboot_server(self, reset_type='GracefulRestart'):
        """
        서버 재부팅
        
        Args:
            reset_type: GracefulRestart, ForceRestart, GracefulShutdown, ForceOff, On
        """
        try:
            url = f"{self.base_url}/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset"
            payload = {'ResetType': reset_type}
            
            response = self.session.post(
                url,
                data=json.dumps(payload),
                headers=self.headers,
                timeout=30
            )
            
            return response.status_code in [200, 202, 204]
            
        except Exception as e:
            print(f"재부팅 오류: {str(e)}")
            return False
    
    def power_on_server(self):
        """서버 전원 켜기"""
        return self.reboot_server('On')
    
    def power_off_server(self, shutdown_type='GracefulShutdown'):
        """
        서버 전원 끄기
        
        Args:
            shutdown_type: GracefulShutdown 또는 ForceOff
        """
        return self.reboot_server(shutdown_type)
