#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Модуль для взаимодействия с API Synology NAS
"""

import requests
import json
import logging
from typing import Dict, Any, Optional, List
import socket
import struct
from time import sleep
import urllib3

from src.config.config import (
    SYNOLOGY_HOST, 
    SYNOLOGY_PORT,
    SYNOLOGY_USERNAME,
    SYNOLOGY_PASSWORD,
    SYNOLOGY_SECURE,
    SYNOLOGY_TIMEOUT,
    SYNOLOGY_MAC,
    WOL_PORT
)

# Отключение предупреждений о небезопасных SSL-соединениях
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

logger = logging.getLogger(__name__)

class SynologyAPI:
    """Класс для взаимодействия с API Synology NAS с использованием python-synology"""
    
    def __init__(self):
        """Инициализация класса SynologyAPI"""
        self.dsm = None
        
    def login(self) -> bool:
        """Авторизация в API Synology NAS используя python-synology"""
        try:
            # Создаем экземпляр SynologyDSM
            self.dsm = SynologyDSM(
                dsm_ip=SYNOLOGY_HOST,
                dsm_port=SYNOLOGY_PORT,
                username=SYNOLOGY_USERNAME,
                password=SYNOLOGY_PASSWORD,
                use_https=SYNOLOGY_SECURE,
                timeout=SYNOLOGY_TIMEOUT,
                verify_ssl=False
            )
            
            # Авторизация
            self.dsm.login()
            logger.info("Successfully logged in to Synology NAS")
            return True
                
        except Exception as e:
            logger.error(f"Failed to log in to Synology NAS: {str(e)}")
            self.dsm = None
            return False
    
    def logout(self) -> bool:
        """Выход из API Synology NAS"""
        if not self.dsm:
            return True
            
        try:
            self.dsm.logout()
            self.dsm = None
            logger.info("Successfully logged out from Synology NAS")
            return True
                
        except Exception as e:
            logger.error(f"Error during logout: {str(e)}")
            return False
    
    def get_system_status(self) -> Optional[Dict[str, Any]]:
        """Получение расширенного статуса системы"""
        if not self.dsm and not self.login():
            return None
            
        try:
            result = {
                "model": self.dsm.information.model,
                "version": self.dsm.information.version_string,
                "uptime": self.dsm.information.uptime,
                "serial": self.dsm.information.serial,
                "temperature": self.dsm.information.temperature,
                "temperature_unit": "C",
                "cpu_usage": self.dsm.utilisation.cpu_total_load,
                "memory": {
                    "total_mb": self.dsm.utilisation.memory_size_mb,
                    "available_mb": self.dsm.utilisation.memory_available_real_mb,
                    "usage_percent": self.dsm.utilisation.memory_real_usage
                },
                "network": self._get_network_info(),
                "volumes": self._get_volumes_info()
            }
            
            logger.info("Successfully fetched extended system status")
            return result
                
        except Exception as e:
            logger.error(f"Error getting system status: {str(e)}")
            return None
    
    def _get_network_info(self) -> List[Dict[str, Any]]:
        """Получение информации о сетевых интерфейсах"""
        try:
            result = []
            
            # Получение информации о сети
            for device in self.dsm.network.interfaces:
                net_info = {
                    "device": device,
                    "ip": self.dsm.network.get_ip(device),
                    "mask": self.dsm.network.get_mask(device),
                    "mac": self.dsm.network.get_mac(device),
                    "type": self.dsm.network.get_type(device),
                    "rx_bytes": self.dsm.network.get_rx(device),
                    "tx_bytes": self.dsm.network.get_tx(device)
                }
                result.append(net_info)
                
            return result
            
        except Exception as e:
            logger.error(f"Error getting network info: {str(e)}")
            return []
    
    def _get_volumes_info(self) -> List[Dict[str, Any]]:
        """Получение информации о томах хранения"""
        try:
            result = []
            
            # Получение информации о томах
            for volume in self.dsm.storage.volumes:
                vol_info = {
                    "name": volume,
                    "status": self.dsm.storage.volume_status(volume),
                    "device_type": self.dsm.storage.volume_device_type(volume),
                    "total_size": self.dsm.storage.volume_size_total(volume),
                    "used_size": self.dsm.storage.volume_size_used(volume),
                    "percent_used": self.dsm.storage.volume_percentage_used(volume)
                }
                result.append(vol_info)
                
            return result
            
        except Exception as e:
            logger.error(f"Error getting volumes info: {str(e)}")
            return []
    
    def get_shared_folders(self) -> List[Dict[str, Any]]:
        """Получение списка общих папок"""
        if not self.dsm and not self.login():
            return []
            
        try:
            result = []
            
            # Получение информации о общих папках
            for folder in self.dsm.share.shares:
                share_info = {
                    "name": folder,
                    "path": self.dsm.share.get_info(folder).get("path", ""),
                    "desc": self.dsm.share.get_info(folder).get("desc", "")
                }
                result.append(share_info)
                
            logger.info(f"Successfully retrieved {len(result)} shared folders")
            return result
                
        except Exception as e:
            logger.error(f"Error getting shared folders: {str(e)}")
            return []
    
    def get_system_info(self) -> Dict[str, Any]:
        """Получение основной информации о системе"""
        if not self.dsm and not self.login():
            return {}
            
        try:
            result = {
                "model": self.dsm.information.model,
                "serial": self.dsm.information.serial,
                "version": self.dsm.information.version_string,
                "uptime": self.dsm.information.uptime
            }
            
            logger.info("Successfully fetched system info")
            return result
                
        except Exception as e:
            logger.error(f"Error getting system info: {str(e)}")
            return {}
    
    def shutdown_system(self) -> bool:
        """Выключение системы"""
        if not self.dsm and not self.login():
            return False
            
        try:
            # Используем низкоуровневый API для отправки команды выключения
            endpoint = "SYNO.DSM.System"
            api_path = "entry.cgi"
            req_param = {"version": 1, "method": "shutdown"}
            
            self.dsm.post(endpoint, api_path, req_param)
            logger.info("Successfully initiated system shutdown")
            return True
                
        except Exception as e:
            logger.error(f"Error shutting down system: {str(e)}")
            return False
    
    def reboot_system(self) -> bool:
        """Перезагрузка системы"""
        if not self.dsm and not self.login():
            return False
            
        try:
            # Используем низкоуровневый API для отправки команды перезагрузки
            endpoint = "SYNO.DSM.System"
            api_path = "entry.cgi"
            req_param = {"version": 1, "method": "reboot"}
            
            self.dsm.post(endpoint, api_path, req_param)
            logger.info("Successfully initiated system reboot")
            return True
                
        except Exception as e:
            logger.error(f"Error rebooting system: {str(e)}")
            return False
    
    def is_online(self) -> bool:
        """Проверка онлайн-статуса Synology NAS"""
        try:
            socket_obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            socket_obj.settimeout(SYNOLOGY_TIMEOUT)
            result = socket_obj.connect_ex((SYNOLOGY_HOST, SYNOLOGY_PORT))
            socket_obj.close()
            
            return result == 0
        except socket.error:
            return False
    
    def wake_on_lan(self) -> bool:
        """Отправка Wake-on-LAN пакета для включения Synology NAS"""
        if not SYNOLOGY_MAC:
            logger.error("MAC address not configured")
            return False
            
        try:
            # Преобразование MAC-адреса в байты
            mac_address = SYNOLOGY_MAC.replace(':', '').replace('-', '')
            mac_bytes = bytes.fromhex(mac_address)
            
            # Создание Magic Packet
            magic_packet = b'\xff' * 6 + mac_bytes * 16
            
            # Отправка пакета
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
            sock.sendto(magic_packet, (SYNOLOGY_HOST, WOL_PORT))
            sock.close()
            
            logger.info(f"Wake-on-LAN packet sent to {SYNOLOGY_MAC}")
            return True
            
        except Exception as e:
            logger.error(f"Error sending Wake-on-LAN packet: {str(e)}")
            return False
            
    def wait_for_boot(self, max_attempts: int = 30, delay: int = 10) -> bool:
        """Ожидание загрузки Synology NAS"""
        logger.info("Waiting for Synology NAS to boot...")
        
        for attempt in range(max_attempts):
            if self.is_online():
                logger.info(f"Synology NAS is online after {attempt + 1} attempts")
                # Дадим дополнительное время для полной загрузки всех сервисов
                sleep(delay)
                return True
                
            sleep(delay)
            logger.info(f"Waiting for boot... Attempt {attempt + 1}/{max_attempts}")
            
        logger.error(f"Synology NAS didn't come online after {max_attempts} attempts")
        return False
        
    def power_on(self) -> bool:
        """Включение Synology NAS"""
        if self.is_online():
            logger.info("Synology NAS is already online")
            return True
            
        # Отправка WoL пакета
        if not self.wake_on_lan():
            return False
            
        # Ожидание загрузки
        return self.wait_for_boot()
        
    def power_off(self) -> bool:
        """Выключение Synology NAS"""
        if not self.is_online():
            logger.info("Synology NAS is already offline")
            return True
            
        return self.shutdown_system()
        
    def get_system_load(self) -> Dict[str, Any]:
        """Получение информации о загрузке системы"""
        if not self.dsm and not self.login():
            return {}
            
        try:
            result = {
                "cpu_load": self.dsm.utilisation.cpu_total_load,
                "memory": {
                    "total_mb": self.dsm.utilisation.memory_size_mb,
                    "available_mb": self.dsm.utilisation.memory_available_real_mb,
                    "cached_mb": self.dsm.utilisation.memory_cached_mb,
                    "usage_percent": self.dsm.utilisation.memory_real_usage
                },
                "network": {}
            }
            
            # Добавляем данные по сети
            for device in self.dsm.network.interfaces:
                result["network"][device] = {
                    "rx_bytes": self.dsm.network.get_rx(device),
                    "tx_bytes": self.dsm.network.get_tx(device)
                }
                
            logger.info("Successfully fetched system load")
            return result
                
        except Exception as e:
            logger.error(f"Error getting system load: {str(e)}")
            return {}
    
    def get_storage_status(self) -> Dict[str, Any]:
        """Получение подробной информации о хранилище"""
        if not self.dsm and not self.login():
            return {}
            
        try:
            result = {
                "volumes": self._get_volumes_info(),
                "disks": self._get_disks_info(),
                "total_size": 0,
                "total_used": 0
            }
            
            # Суммируем общий размер и использование
            for volume in result["volumes"]:
                result["total_size"] += volume["total_size"]
                result["total_used"] += volume["used_size"]
                
            logger.info("Successfully fetched storage status")
            return result
                
        except Exception as e:
            logger.error(f"Error getting storage status: {str(e)}")
            return {}
            
    def _get_disks_info(self) -> List[Dict[str, Any]]:
        """Получение информации о дисках"""
        try:
            result = []
            
            # Получение информации о дисках
            for disk in self.dsm.storage.disks:
                disk_info = {
                    "name": disk,
                    "model": self.dsm.storage.disk_model(disk),
                    "type": self.dsm.storage.disk_type(disk),
                    "status": self.dsm.storage.disk_status(disk),
                    "temp": self.dsm.storage.disk_temp(disk)
                }
                result.append(disk_info)
                
            return result
            
        except Exception as e:
            logger.error(f"Error getting disks info: {str(e)}")
            return []
            
    def get_security_status(self) -> Dict[str, bool]:
        """Получение информации о состоянии безопасности"""
        if not self.dsm and not self.login():
            return {"success": False}
            
        try:
            # Используем низкоуровневый API для получения информации о безопасности
            endpoint = "SYNO.Core.Security.DSM"
            api_path = "entry.cgi"
            req_param = {"version": 1, "method": "status"}
            
            response = self.dsm.get(endpoint, api_path, req_param)
            
            if response and "data" in response:
                return {
                    "success": True,
                    "status": response["data"].get("status", "unknown"),
                    "last_check": response["data"].get("last_check", None),
                    "is_secure": response["data"].get("is_secure", False)
                }
            else:
                return {"success": False}
                
        except Exception as e:
            logger.error(f"Error getting security status: {str(e)}")
            return {"success": False}
            
    def get_users(self) -> List[str]:
        """Получение списка пользователей"""
        if not self.dsm and not self.login():
            return []
            
        try:
            users = []
            
            # Используем низкоуровневый API для получения списка пользователей
            endpoint = "SYNO.Core.User"
            api_path = "entry.cgi"
            req_param = {"version": 1, "method": "list", "additional": ["email"]}
            
            response = self.dsm.get(endpoint, api_path, req_param)
            
            if response and "data" in response and "users" in response["data"]:
                for user in response["data"]["users"]:
                    if "name" in user:
                        users.append(user["name"])
                        
            logger.info(f"Successfully retrieved {len(users)} users")
            return users
                
        except Exception as e:
            logger.error(f"Error getting users: {str(e)}")
            return []
