#!/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"""
    
    def __init__(self):
        """Инициализация класса SynologyAPI"""
        self.protocol = "https" if SYNOLOGY_SECURE else "http"
        self.base_url = f"{self.protocol}://{SYNOLOGY_HOST}:{SYNOLOGY_PORT}/webapi"
        self.sid = None
        self.session = requests.Session()
        
    def login(self) -> bool:
        """Авторизация в API Synology NAS"""
        try:
            url = f"{self.base_url}/auth.cgi"
            params = {
                "api": "SYNO.API.Auth",
                "version": "3",
                "method": "login",
                "account": SYNOLOGY_USERNAME,
                "passwd": SYNOLOGY_PASSWORD,
                "session": "SynologyPowerControlBot",
                "format": "cookie"
            }
            
            response = self.session.get(url, params=params, timeout=SYNOLOGY_TIMEOUT, verify=False)
            data = response.json()
            
            if data.get("success"):
                self.sid = data.get("data", {}).get("sid")
                logger.info("Successfully logged in to Synology NAS")
                return True
            else:
                error_code = data.get("error", {}).get("code", -1)
                logger.error(f"Failed to log in to Synology NAS: Error code {error_code}")
                return False
                
        except requests.RequestException as e:
            logger.error(f"Connection error: {str(e)}")
            return False
    
    def logout(self) -> bool:
        """Выход из API Synology NAS"""
        if not self.sid:
            return True
            
        try:
            url = f"{self.base_url}/auth.cgi"
            params = {
                "api": "SYNO.API.Auth",
                "version": "1",
                "method": "logout",
                "session": "SynologyPowerControlBot",
                "_sid": self.sid
            }
            
            response = self.session.get(url, params=params, timeout=SYNOLOGY_TIMEOUT, verify=False)
            data = response.json()
            
            if data.get("success"):
                self.sid = None
                logger.info("Successfully logged out from Synology NAS")
                return True
            else:
                error_code = data.get("error", {}).get("code", -1)
                logger.error(f"Failed to log out from Synology NAS: Error code {error_code}")
                return False
                
        except requests.RequestException as e:
            logger.error(f"Connection error: {str(e)}")
            return False
    
    def get_system_status(self) -> Optional[Dict[str, Any]]:
        """Получение статуса системы"""
        # Если устройство недоступно, сразу возвращаем минимальную информацию
        if not self.is_online():
            logger.info("Device is offline, skipping API request")
            return {"status": "offline"}
            
        if not self.sid and not self.login():
            logger.warning("Not authenticated, returning minimal status")
            return {"status": "unknown", "error": "authentication_failed"}
            
        try:
            url = f"{self.base_url}/entry.cgi"
            params = {
                "api": "SYNO.DSM.Info",
                "version": "1",
                "method": "getinfo",
                "_sid": self.sid
            }
            
            response = self.session.get(url, params=params, timeout=SYNOLOGY_TIMEOUT, verify=False)
            data = response.json()
            
            if data.get("success"):
                logger.info("Successfully fetched system status")
                return data.get("data", {})
            else:
                error_code = data.get("error", {}).get("code", -1)
                logger.error(f"Failed to get system status: Error code {error_code}")
                
                # Ошибка 104 - требуется авторизация или неверное API
                if error_code == 104:
                    # Пробуем переавторизоваться
                    if self.login():
                        logger.info("Re-authenticated, trying again")
                        return self.get_system_status()
                        
                # Возвращаем минимальную информацию с ошибкой
                return {
                    "status": "error", 
                    "error_code": error_code,
                    "is_online": True
                }
                
        except requests.RequestException as e:
            logger.error(f"Connection error: {str(e)}")
            return {"status": "error", "error": str(e), "is_online": True}
        except Exception as e:
            logger.error(f"Unexpected error getting status: {str(e)}")
            return {"status": "error", "error": str(e), "is_online": True}
    
    def shutdown_system(self) -> bool:
        """Выключение системы"""
        if not self.sid and not self.login():
            return False
            
        try:
            url = f"{self.base_url}/entry.cgi"
            params = {
                "api": "SYNO.DSM.System",
                "version": "1",
                "method": "shutdown",
                "_sid": self.sid
            }
            
            response = self.session.get(url, params=params, timeout=SYNOLOGY_TIMEOUT, verify=False)
            data = response.json()
            
            if data.get("success"):
                logger.info("Successfully initiated system shutdown")
                return True
            else:
                error_code = data.get("error", {}).get("code", -1)
                logger.error(f"Failed to shutdown system: Error code {error_code}")
                return False
                
        except requests.RequestException as e:
            logger.error(f"Connection error: {str(e)}")
            return False
    
    def reboot_system(self) -> bool:
        """Перезагрузка системы"""
        if not self.sid and not self.login():
            return False
            
        try:
            url = f"{self.base_url}/entry.cgi"
            params = {
                "api": "SYNO.DSM.System",
                "version": "1",
                "method": "reboot",
                "_sid": self.sid
            }
            
            response = self.session.get(url, params=params, timeout=SYNOLOGY_TIMEOUT, verify=False)
            data = response.json()
            
            if data.get("success"):
                logger.info("Successfully initiated system reboot")
                return True
            else:
                error_code = data.get("error", {}).get("code", -1)
                logger.error(f"Failed to reboot system: Error code {error_code}")
                return False
                
        except requests.RequestException as e:
            logger.error(f"Connection error: {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_shared_folders(self) -> List[Dict[str, Any]]:
        """Получение списка общих папок (заглушка)"""
        logger.warning("Function get_shared_folders() is not implemented yet")
        return []
        
    def get_system_load(self) -> Dict[str, Any]:
        """Получение информации о загрузке системы (заглушка)"""
        logger.warning("Function get_system_load() is not implemented yet")
        return {}
        
    def get_storage_status(self) -> Dict[str, Any]:
        """Получение подробной информации о хранилище (заглушка)"""
        logger.warning("Function get_storage_status() is not implemented yet")
        return {}
        
    def get_security_status(self) -> Dict[str, bool]:
        """Получение информации о состоянии безопасности (заглушка)"""
        logger.warning("Function get_security_status() is not implemented yet")
        return {"success": False}
