"""
Implementación de File para sistema de archivos local.
"""
import os
from pathlib import Path
from datetime import datetime
from typing import Dict, Any, BinaryIO, TYPE_CHECKING
import shutil

from tai_alphi import Alphi
from tai_storage.base import File
from tai_storage.exceptions import FileNotFoundError, FileAlreadyExistsError

if TYPE_CHECKING:
    from .folder import LocalFolder

logger = Alphi.get_logger_by_name('tai-storage')

class LocalFile(File):
    """
    Implementación de File para archivos en el sistema de archivos local.
    """
    
    def __init__(self, folder: 'LocalFolder', filename: str, full_path: str):
        """
        Inicializa un archivo local.
        
        Args:
            folder: LocalFolder al que pertenece
            filename: Nombre del archivo
            full_path: Ruta completa del archivo
        """
        super().__init__(folder, filename, full_path)
        self._path = Path(full_path)
    
    @property
    def name(self) -> str:
        """Nombre del archivo sin extensión."""
        return self._path.stem
    
    @property
    def extension(self) -> str:
        """Extensión del archivo incluyendo el punto."""
        return self._path.suffix
    
    @property
    def size(self) -> int:
        """Tamaño del archivo en bytes."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        return self._path.stat().st_size
    
    @property
    def created_time(self) -> datetime:
        """Fecha y hora de creación del archivo."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        timestamp = self._path.stat().st_ctime
        return datetime.fromtimestamp(timestamp)
    
    @property
    def modified_time(self) -> datetime:
        """Fecha y hora de última modificación."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        timestamp = self._path.stat().st_mtime
        return datetime.fromtimestamp(timestamp)
    
    def read(self) -> bytes:
        """Lee el contenido completo del archivo."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        
        logger.debug(f"Reading file: {self.full_path}")
        return self._path.read_bytes()
    
    def read_text(self, encoding: str = 'utf-8') -> str:
        """Lee el contenido del archivo como texto."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        
        logger.debug(f"Reading text file: {self.full_path}")
        return self._path.read_text(encoding=encoding)
    
    def read_stream(self) -> BinaryIO:
        """Obtiene un stream de lectura del archivo."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        
        logger.debug(f"Opening file stream: {self.full_path}")
        return open(self._path, 'rb')
    
    def write(self, data: bytes, overwrite: bool = True) -> None:
        """Escribe datos en el archivo."""
        if self.exists() and not overwrite:
            raise FileAlreadyExistsError(f"File already exists: {self.full_path}")
        
        # Crear directorio padre si no existe
        self._path.parent.mkdir(parents=True, exist_ok=True)
        
        logger.debug(f"Writing file: {self.full_path} ({len(data)} bytes)")
        self._path.write_bytes(data)
    
    def write_text(self, text: str, encoding: str = 'utf-8', overwrite: bool = True) -> None:
        """Escribe texto en el archivo."""
        if self.exists() and not overwrite:
            raise FileAlreadyExistsError(f"File already exists: {self.full_path}")
        
        # Crear directorio padre si no existe
        self._path.parent.mkdir(parents=True, exist_ok=True)
        
        logger.debug(f"Writing text file: {self.full_path}")
        self._path.write_text(text, encoding=encoding)
    
    def delete(self) -> None:
        """Elimina el archivo."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        
        logger.info(f"Deleting file: {self.full_path}")
        self._path.unlink()
    
    def exists(self) -> bool:
        """Verifica si el archivo existe."""
        return self._path.is_file()
    
    def copy_to(self, destination: File, overwrite: bool = False) -> None:
        """Copia el archivo a otro destino."""
        if not self.exists():
            raise FileNotFoundError(f"Source file not found: {self.full_path}")
        
        if destination.exists() and not overwrite:
            raise FileAlreadyExistsError(f"Destination file already exists: {destination.full_path}")
        
        logger.info(f"Copying file: {self.full_path} -> {destination.full_path}")
        
        # Leer datos y escribir en destino
        data = self.read()
        destination.write(data, overwrite=overwrite)
    
    def move_to(self, destination: File, overwrite: bool = False) -> None:
        """Mueve el archivo a otro destino."""
        if not self.exists():
            raise FileNotFoundError(f"Source file not found: {self.full_path}")
        
        if destination.exists() and not overwrite:
            raise FileAlreadyExistsError(f"Destination file already exists: {destination.full_path}")
        
        logger.info(f"Moving file: {self.full_path} -> {destination.full_path}")
        
        # Si el destino es también LocalFile y está en el mismo sistema, usar rename
        if isinstance(destination, LocalFile):
            dest_path = Path(destination.full_path)
            dest_path.parent.mkdir(parents=True, exist_ok=True)
            
            if overwrite and dest_path.exists():
                dest_path.unlink()
            
            shutil.move(str(self._path), str(dest_path))
        else:
            # Copiar y luego eliminar
            self.copy_to(destination, overwrite=overwrite)
            self.delete()
    
    def get_metadata(self) -> Dict[str, Any]:
        """Obtiene todos los metadatos del archivo."""
        if not self.exists():
            raise FileNotFoundError(f"File not found: {self.full_path}")
        
        stat = self._path.stat()
        
        return {
            'name': self.filename,
            'path': self.full_path,
            'size': stat.st_size,
            'extension': self.extension,
            'created_time': datetime.fromtimestamp(stat.st_ctime),
            'modified_time': datetime.fromtimestamp(stat.st_mtime),
            'is_file': True,
            'is_symlink': self._path.is_symlink(),
            'permissions': oct(stat.st_mode),
        }
