"""
Dataset Split Files Client for downloading .wav and .npz files for training
Uses KWS API only - no direct MinIO connection, no presigned URLs
All file access goes through backend API endpoints
"""

from typing import Optional, List, Dict, Any
from pathlib import Path
import numpy as np
import logging
import requests

logger = logging.getLogger(__name__)


class DatasetSplitFilesClient:
    """
    Client for downloading audio and feature files from dataset splits via API.
    This replaces direct MinIO connection - all file access goes through KWS API.
    
    Example:
        >>> from kwslib import KWSClient, DatasetSplitFilesClient
        >>> 
        >>> # Initialize API client
        >>> api = KWSClient(base_url="http://localhost:8000")
        >>> api.login(username="admin", password="password")
        >>> 
        >>> # Initialize files client (uses API client)
        >>> files_client = DatasetSplitFilesClient(api)
        >>> 
        >>> # List files in split
        >>> files = files_client.list_files(split_id=1, file_type="npz")
        >>> 
        >>> # Download all .npz files
        >>> files_client.download_all_npz(split_id=1, output_dir="features")
    """
    
    def __init__(self, api_client):
        """
        Initialize dataset split files client.
        
        Args:
            api_client: KWSClient instance (must be authenticated)
        """
        self.api = api_client
    
    def list_files(
        self,
        split_id: int,
        file_type: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        List all files (.wav or .npz) in dataset split.
        
        Args:
            split_id: Dataset split ID
            file_type: Filter by file type ('wav' or 'npz'), None for all
            
        Returns:
            Dictionary with file list and metadata
        """
        return self.api.dataset_splits.list_files(split_id, file_type)
    
    def download_wav(
        self,
        split_id: int,
        file_id: int,
        output_path: str,
    ) -> str:
        """
        Download a .wav file from dataset split.
        
        Args:
            split_id: Dataset split ID
            file_id: File ID
            output_path: Local path to save the file
            
        Returns:
            Path to downloaded file
        """
        return self.api.dataset_splits.download_file(
            split_id=split_id,
            file_id=file_id,
            file_type="wav",
            output_path=output_path
        )
    
    def download_npz(
        self,
        split_id: int,
        file_id: int,
        output_path: Optional[str] = None,
    ) -> dict:
        """
        Download a .npz file from dataset split and load as numpy dict.
        
        Args:
            split_id: Dataset split ID
            file_id: File ID
            output_path: Optional local path to save the file
            
        Returns:
            Dictionary of numpy arrays loaded from .npz file
        """
        if output_path:
            file_path = self.api.dataset_splits.download_file(
                split_id=split_id,
                file_id=file_id,
                file_type="npz",
                output_path=output_path
            )
            data = np.load(file_path)
        else:
            # Download to temporary file
            import tempfile
            with tempfile.NamedTemporaryFile(delete=False, suffix='.npz') as tmp:
                file_path = self.api.dataset_splits.download_file(
                    split_id=split_id,
                    file_id=file_id,
                    file_type="npz",
                    output_path=tmp.name
                )
                data = np.load(file_path)
                import os
                os.unlink(file_path)  # Clean up temp file
        
        # Convert to dict
        result = {key: data[key] for key in data.keys()}
        logger.info(f"Downloaded and loaded npz file {file_id}")
        return result
    
    def download_all_wav(
        self,
        split_id: int,
        output_dir: str,
    ) -> List[str]:
        """
        Download all .wav files for a dataset split.
        
        Args:
            split_id: Dataset split ID
            output_dir: Directory to save audio files
            
        Returns:
            List of paths to downloaded files
        """
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Get file list
        files_info = self.list_files(split_id, file_type="wav")
        wav_files = [f for f in files_info.get("files", []) if f.get("file_type") == "wav"]
        
        downloaded_files = []
        for file_info in wav_files:
            output_path = output_dir / file_info["file_name"]
            try:
                # Use API endpoint instead of presigned URL to avoid signature issues
                # Try different possible keys for file_id
                file_id = (
                    file_info.get("file_id") or 
                    file_info.get("id") or 
                    file_info.get("keyword_audio_sample_id") or
                    file_info.get("sample_id")
                )
                
                if file_id:
                    # Download via API endpoint (handles authentication and proxy)
                    logger.debug(f"Downloading file {file_info['file_name']} (ID: {file_id}) via API")
                    self.api.dataset_splits.download_file(
                        split_id=split_id,
                        file_id=file_id,
                        file_type="wav",
                        output_path=str(output_path)
                    )
                    downloaded_files.append(str(output_path))
                    logger.debug(f"Successfully downloaded {file_info['file_name']}")
                else:
                    logger.warning(f"No file_id found for {file_info['file_name']}, skipping")
                    continue
            except requests.RequestException as e:
                # Check if error is related to MinIO connection (should not happen)
                error_str = str(e).lower()
                if 'minio' in error_str or ':9000' in error_str or 'getaddrinfo failed' in error_str:
                    logger.error(
                        f"Error downloading {file_info['file_name']}: Attempted to connect to MinIO directly. "
                        f"This should not happen - backend should stream files via API. Error: {e}"
                    )
                else:
                    logger.error(f"Error downloading {file_info['file_name']}: {e}")
                continue
            except Exception as e:
                logger.error(f"Error downloading {file_info['file_name']}: {e}")
                continue
        
        logger.info(f"Downloaded {len(downloaded_files)} wav files for split {split_id}")
        return downloaded_files
    
    def download_all_npz(
        self,
        split_id: int,
        output_dir: str,
        skip_existing: bool = True,
    ) -> List[str]:
        """
        Download all .npz files for a dataset split.
        Lấy danh sách file từ DB, so với thư mục output_dir: file đã có thì bỏ qua, thiếu thì tải.

        Args:
            split_id: Dataset split ID
            output_dir: Directory to save feature files (and to check for existing files)
            skip_existing: Nếu True (mặc định), bỏ qua file đã tồn tại trong output_dir

        Returns:
            List of paths to files (downloaded or already present)
        """
        output_dir = Path(output_dir)
        output_dir.mkdir(parents=True, exist_ok=True)
        
        # Get file list from API
        files_info = self.list_files(split_id, file_type="npz")
        npz_files = [f for f in files_info.get("files", []) if f.get("file_type") == "npz"]
        
        downloaded_files = []
        skipped = 0
        for file_info in npz_files:
            output_path = output_dir / file_info["file_name"]
            if skip_existing and output_path.exists():
                downloaded_files.append(str(output_path))
                skipped += 1
                logger.debug(f"Đã có sẵn, bỏ qua: {file_info['file_name']}")
                continue
            try:
                # Use API endpoint instead of presigned URL to avoid signature issues
                # Try different possible keys for file_id
                file_id = (
                    file_info.get("file_id") or 
                    file_info.get("id") or 
                    file_info.get("keyword_audio_feature_id") or
                    file_info.get("feature_id")
                )
                
                if file_id:
                    # Download via API endpoint (handles authentication and proxy)
                    logger.debug(f"Downloading file {file_info['file_name']} (ID: {file_id}) via API")
                    self.api.dataset_splits.download_file(
                        split_id=split_id,
                        file_id=file_id,
                        file_type="npz",
                        output_path=str(output_path)
                    )
                    downloaded_files.append(str(output_path))
                    logger.debug(f"Successfully downloaded {file_info['file_name']}")
                else:
                    logger.warning(f"No file_id found for {file_info['file_name']}, skipping")
                    continue
            except requests.RequestException as e:
                # Check if error is related to MinIO connection (should not happen)
                error_str = str(e).lower()
                if 'minio' in error_str or ':9000' in error_str or 'getaddrinfo failed' in error_str:
                    logger.error(
                        f"Error downloading {file_info['file_name']}: Attempted to connect to MinIO directly. "
                        f"This should not happen - backend should stream files via API. Error: {e}"
                    )
                else:
                    logger.error(f"Error downloading {file_info['file_name']}: {e}")
                continue
            except Exception as e:
                logger.error(f"Error downloading {file_info['file_name']}: {e}")
                continue
        
        if skipped:
            logger.info(f"Split {split_id}: {len(downloaded_files) - skipped} tải mới, {skipped} đã có sẵn.")
        else:
            logger.info(f"Downloaded {len(downloaded_files)} npz files for split {split_id}")
        return downloaded_files
    
    def download_all_files_zip(
        self,
        split_id: int,
        file_type: str,
        output_path: str,
    ) -> str:
        """
        Download all files as ZIP from dataset split.
        
        Args:
            split_id: Dataset split ID
            file_type: File type ('wav' or 'npz')
            output_path: Path to save ZIP file
            
        Returns:
            Path to downloaded ZIP file
        """
        return self.api.dataset_splits.download_all_files(
            split_id=split_id,
            file_type=file_type,
            format="zip",
            output_path=output_path
        )
    
    def get_file_urls(
        self,
        split_id: int,
        file_type: str,
    ) -> Dict[str, Any]:
        """
        Get file information (not presigned URLs) for all files in dataset split.
        Note: This returns file metadata, not direct MinIO URLs.
        Use download_file() or download_all_*() methods to download files via API.
        
        Args:
            split_id: Dataset split ID
            file_type: File type ('wav' or 'npz')
            
        Returns:
            Dictionary with list of file information (includes file_id for API downloads)
        """
        # Get file list with metadata (includes file_id for API downloads)
        return self.api.dataset_splits.list_files(
            split_id=split_id,
            file_type=file_type
        )
