import os
import shutil
from pathlib import Path
import platform
from datetime import datetime
from typing import List, Dict, Any

class FileManager:
    def __init__(self, base_dir: str = None):
        if base_dir is None:
            self.current_directory = Path.cwd()
        else:
            self.current_directory = Path(base_dir).resolve()
    
    @property
    def current_directory(self) -> Path:
        return self._current_directory
    
    @current_directory.setter
    def current_directory(self, value: Path):
        if value.exists() and value.is_dir():
            self._current_directory = value
        else:
            raise ValueError(f"Директория не существует: {value}")
    
    def list_files(self, path: str = None) -> List[str]:
        """Список файлов и папок"""
        target_path = self._resolve_path(path)
        
        if not target_path.exists():
            raise FileNotFoundError(f"Путь не существует: {target_path}")
        if not target_path.is_dir():
            raise NotADirectoryError(f"Не директория: {target_path}")
        
        items = []
        
        # Родительская директория
        if target_path != target_path.parent:
            items.append("../")
        
        # Все элементы
        for item in sorted(target_path.iterdir()):
            if item.is_dir():
                items.append(f"{item.name}/")
            else:
                items.append(item.name)
        
        return items
    
    def change_directory(self, path: str) -> str:
        """Сменить директорию"""
        new_path = self._resolve_path(path)
        
        if not new_path.exists():
            return f"❌ Путь не существует: {path}"
        if not new_path.is_dir():
            return f"❌ Не директория: {path}"
        
        self.current_directory = new_path
        return f"📁 Текущая директория: {self.current_directory}"
    
    def create_directory(self, name: str) -> str:
        """Создать директорию"""
        new_dir = self.current_directory / name
        
        try:
            new_dir.mkdir(exist_ok=True)
            return f"✅ Создана директория: {new_dir}"
        except Exception as e:
            return f"❌ Ошибка создания директории: {e}"
    
    def create_file(self, name: str) -> str:
        """Создать файл"""
        new_file = self.current_directory / name
        
        try:
            new_file.touch(exist_ok=True)
            return f"✅ Создан файл: {new_file}"
        except Exception as e:
            return f"❌ Ошибка создания файла: {e}"
    
    def copy_file(self, source: str, destination: str) -> str:
        """Копировать файл"""
        src = self._resolve_path(source)
        dst = self._resolve_path(destination)
        
        try:
            if src.is_dir():
                shutil.copytree(src, dst)
            else:
                shutil.copy2(src, dst)
            return f"✅ Скопировано: {src} -> {dst}"
        except Exception as e:
            return f"❌ Ошибка копирования: {e}"
    
    def move_file(self, source: str, destination: str) -> str:
        """Переместить файл"""
        src = self._resolve_path(source)
        dst = self._resolve_path(destination)
        
        try:
            shutil.move(str(src), str(dst))
            return f"✅ Перемещено: {src} -> {dst}"
        except Exception as e:
            return f"❌ Ошибка перемещения: {e}"
    
    def delete_file(self, path: str) -> str:
        """Удалить файл/директорию"""
        target = self._resolve_path(path)
        
        try:
            if target.is_dir():
                shutil.rmtree(target)
            else:
                target.unlink()
            return f"✅ Удалено: {target}"
        except Exception as e:
            return f"❌ Ошибка удаления: {e}"
    
    def find_files(self, name: str, search_path: str = None) -> List[str]:
        """Найти файлы по имени"""
        start_path = self._resolve_path(search_path) if search_path else self.current_directory
        
        results = []
        for root, dirs, files in os.walk(start_path):
            for file in files:
                if name.lower() in file.lower():
                    results.append(str(Path(root) / file))
        
        return results[:20]  # Ограничиваем вывод
    
    def get_file_info(self, path: str) -> Dict[str, Any]:
        """Получить информацию о файле"""
        target = self._resolve_path(path)
        
        if not target.exists():
            return {"error": f"Файл не существует: {path}"}
        
        stat = target.stat()
        created = datetime.fromtimestamp(stat.st_ctime)
        modified = datetime.fromtimestamp(stat.st_mtime)
        
        return {
            "name": target.name,
            "path": str(target),
            "size": stat.st_size,
            "size_human": self._human_size(stat.st_size),
            "created": created.strftime("%Y-%m-%d %H:%M:%S"),
            "modified": modified.strftime("%Y-%m-%d %H:%M:%S"),
            "is_dir": target.is_dir(),
            "is_file": target.is_file(),
            "extension": target.suffix if target.is_file() else "",
        }
    
    def get_disk_info(self) -> Dict[str, Any]:
        """Информация о дисках"""
        if platform.system() == "Windows":
            return self._get_windows_disk_info()
        else:
            return self._get_unix_disk_info()
    
    def _get_windows_disk_info(self) -> Dict[str, Any]:
        """Информация о дисках Windows"""
        import ctypes
        import string
        
        drives = []
        bitmask = ctypes.windll.kernel32.GetLogicalDrives()
        
        for letter in string.ascii_uppercase:
            if bitmask & 1:
                drive_path = f"{letter}:\\"
                try:
                    total, free = self._get_drive_space(drive_path)
                    used = total - free
                    usage_percent = (used / total * 100) if total > 0 else 0
                    
                    drives.append({
                        "drive": drive_path,
                        "total": total,
                        "free": free,
                        "used": used,
                        "usage_percent": round(usage_percent, 1),
                        "total_human": self._human_size(total),
                        "free_human": self._human_size(free),
                        "used_human": self._human_size(used),
                    })
                except:
                    pass
            bitmask >>= 1
        
        return {"drives": drives}
    
    def _get_unix_disk_info(self) -> Dict[str, Any]:
        """Информация о дисках Linux/Mac"""
        import shutil
        
        drives = []
        for mount in ["/", "/home", "/tmp"]:
            if Path(mount).exists():
                try:
                    usage = shutil.disk_usage(mount)
                    total = usage.total
                    free = usage.free
                    used = usage.used
                    usage_percent = (used / total * 100) if total > 0 else 0
                    
                    drives.append({
                        "mount": mount,
                        "total": total,
                        "free": free,
                        "used": used,
                        "usage_percent": round(usage_percent, 1),
                        "total_human": self._human_size(total),
                        "free_human": self._human_size(free),
                        "used_human": self._human_size(used),
                    })
                except:
                    pass
        
        return {"drives": drives}
    
    def _get_drive_space(self, drive_path: str):
        """Получить размер диска"""
        import ctypes
        
        total_bytes = ctypes.c_ulonglong(0)
        free_bytes = ctypes.c_ulonglong(0)
        
        ctypes.windll.kernel32.GetDiskFreeSpaceExW(
            drive_path,
            None,
            ctypes.byref(total_bytes),
            ctypes.byref(free_bytes)
        )
        
        return total_bytes.value, free_bytes.value
    
    def read_file(self, path: str, limit_lines: int = 50) -> str:
        """Прочитать файл"""
        target = self._resolve_path(path)
        
        if not target.exists():
            return f"❌ Файл не существует: {path}"
        if target.is_dir():
            return f"❌ Это директория, а не файл: {path}"
        
        try:
            with open(target, 'r', encoding='utf-8', errors='ignore') as f:
                lines = []
                for i, line in enumerate(f):
                    if i >= limit_lines:
                        lines.append(f"... (показано {limit_lines} строк из файла)")
                        break
                    lines.append(line.rstrip())
                
                return "\n".join(lines)
        except Exception as e:
            return f"❌ Ошибка чтения файла: {e}"
    
    def _resolve_path(self, path: str) -> Path:
        """Преобразовать путь в абсолютный"""
        if path is None:
            return self.current_directory
        
        if Path(path).is_absolute():
            return Path(path).resolve()
        else:
            return (self.current_directory / path).resolve()
    
    def _human_size(self, size_bytes: int) -> str:
        """Преобразовать размер в человекочитаемый формат"""
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if size_bytes < 1024.0:
                return f"{size_bytes:.1f} {unit}"
            size_bytes /= 1024.0
        return f"{size_bytes:.1f} PB"