import logging
import os
import asyncio
from datetime import datetime
from typing import List, Dict, Optional, Any
from urllib.parse import urlparse
from ftplib import FTP
from concurrent.futures import ThreadPoolExecutor
import io

from app.tenant_config import TenantSettings

logger = logging.getLogger(__name__)


# Thread pool for running synchronous FTP operations
_executor = ThreadPoolExecutor(max_workers=4)


class FTPConnector:
    """
    Connector for FTP operations with Smurfit FTP server.
    Uses ftplib (which is more reliable with Windows FTP servers) wrapped in asyncio.
    """

    def __init__(self, settings: TenantSettings):
        self.settings = settings
        self._validate_config()

    def _validate_config(self):
        """Validate that required FTP configuration is present."""
        if not self.settings.SMURFIT_FTP_SERVER:
            raise ValueError(
                "SMURFIT_FTP_SERVER is not configured. "
                "Please set the FTP server URL in your environment variables."
            )
        if not self.settings.SMURFIT_FTP_PASSWORD:
            raise ValueError(
                "SMURFIT_FTP_PASSWORD is not configured. "
                "Please set the FTP password in your environment variables."
            )

    def _get_hostname(self) -> str:
        """Extract hostname from FTP server URL."""
        parsed_url = urlparse(self.settings.SMURFIT_FTP_SERVER)
        return parsed_url.hostname or parsed_url.path.split('/')[0]

    def _list_files_sync(self, remote_path: str) -> List[Dict[str, Any]]:
        """Synchronous FTP file listing using ftplib."""
        hostname = self._get_hostname()
        ftp = None

        try:
            # Connect to FTP server
            ftp = FTP()
            ftp.connect(hostname, self.settings.SMURFIT_FTP_PORT)
            ftp.login(self.settings.SMURFIT_FTP_USERNAME, self.settings.SMURFIT_FTP_PASSWORD)

            # Change to target directory
            if remote_path and remote_path != "/":
                ftp.cwd(remote_path)

            # Use NLST to get simple filename list (most reliable for Windows FTP)
            filenames = ftp.nlst()

            # Filter for CSV files
            files = []
            for filename in filenames:
                if filename.endswith('.csv'):
                    files.append({
                        "name": filename,
                        "size": 0,  # NLST doesn't provide size
                        "modify": ""  # NLST doesn't provide modification time
                    })

            logger.debug(f"Found {len(files)} CSV files in {remote_path}")
            return files

        except Exception as e:
            logger.error(f"FTP list operation failed: {e}")
            raise
        finally:
            if ftp:
                try:
                    ftp.quit()
                except:
                    pass

    async def list_files(self, remote_path: str = "/") -> List[Dict[str, Any]]:
        """
        List files in a remote directory (async wrapper around synchronous ftplib).

        Args:
            remote_path: Remote directory path to list

        Returns:
            List of dictionaries containing file information (name, size, modified time)
        """
        hostname = self._get_hostname()
        logger.info(f"Listing files in {remote_path} on FTP server {hostname}")

        try:
            # Run synchronous FTP operation in thread pool
            loop = asyncio.get_event_loop()
            files = await loop.run_in_executor(_executor, self._list_files_sync, remote_path)
            logger.info(f"Found {len(files)} files in {remote_path}")
            return files

        except Exception as e:
            logger.error(f"Failed to list files in {remote_path}: {e}")
            raise Exception(f"FTP list failed: {str(e)}")

    def _download_file_sync(self, remote_path: str, local_path: str) -> bool:
        """Synchronous FTP file download using ftplib."""
        hostname = self._get_hostname()
        ftp = None

        try:
            # Ensure local directory exists
            local_dir = os.path.dirname(local_path)
            if local_dir:
                os.makedirs(local_dir, exist_ok=True)

            # Connect to FTP server
            ftp = FTP()
            ftp.connect(hostname, self.settings.SMURFIT_FTP_PORT)
            ftp.login(self.settings.SMURFIT_FTP_USERNAME, self.settings.SMURFIT_FTP_PASSWORD)

            # Download the file
            with open(local_path, 'wb') as f:
                ftp.retrbinary(f'RETR {remote_path}', f.write)

            logger.debug(f"Downloaded {remote_path} to {local_path}")
            return True

        except Exception as e:
            logger.error(f"FTP download operation failed: {e}")
            return False
        finally:
            if ftp:
                try:
                    ftp.quit()
                except:
                    pass

    async def download_file(self, remote_path: str, local_path: str) -> bool:
        """
        Download a file from the FTP server (async wrapper).

        Args:
            remote_path: Full path to the remote file (including filename)
            local_path: Full local path where the file should be saved

        Returns:
            True if download was successful, False otherwise
        """
        hostname = self._get_hostname()
        logger.info(f"Downloading {remote_path} from FTP server {hostname}")

        try:
            # Run synchronous FTP operation in thread pool
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(_executor, self._download_file_sync, remote_path, local_path)

            if result:
                logger.info(f"Successfully downloaded {remote_path} to {local_path}")
            return result

        except Exception as e:
            logger.error(f"Failed to download {remote_path}: {e}")
            return False

    def _upload_content_sync(self, content: str, remote_path: str, remote_filename: str) -> Dict[str, Any]:
        """Synchronous FTP content upload using ftplib."""
        hostname = self._get_hostname()
        ftp = None

        try:
            # Connect to FTP server
            ftp = FTP()
            ftp.connect(hostname, self.settings.SMURFIT_FTP_PORT)
            ftp.login(self.settings.SMURFIT_FTP_USERNAME, self.settings.SMURFIT_FTP_PASSWORD)

            # Change to target directory
            if remote_path and remote_path != "/":
                ftp.cwd(remote_path)

            # Convert content to bytes and upload
            content_bytes = content.encode('utf-8')
            bio = io.BytesIO(content_bytes)

            ftp.storbinary(f'STOR {remote_filename}', bio)

            logger.debug(f"Uploaded {remote_filename} to {remote_path}")

            return {
                "server": hostname,
                "path": remote_path,
                "filename": remote_filename,
                "status": "uploaded",
                "message": f"File {remote_filename} successfully uploaded to {remote_path}",
                "timestamp": datetime.now().isoformat(),
                "file_size": len(content_bytes)
            }

        except Exception as e:
            logger.error(f"FTP upload operation failed: {e}")
            raise Exception(f"FTP upload failed: {str(e)}")
        finally:
            if ftp:
                try:
                    ftp.quit()
                except:
                    pass

    async def upload_content(self, content: str, remote_path: str, remote_filename: str) -> Dict[str, Any]:
        """
        Upload string content directly to the FTP server (async wrapper).

        Args:
            content: String content to upload
            remote_path: Remote directory path (e.g., '/ENTR/Orders')
            remote_filename: Name for the file on the remote server

        Returns:
            Dict containing upload result information

        Raises:
            Exception: If upload fails
        """
        hostname = self._get_hostname()
        logger.info(f"Uploading content to {remote_path}/{remote_filename} on FTP server {hostname}")

        try:
            # Run synchronous FTP operation in thread pool
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(
                _executor,
                self._upload_content_sync,
                content,
                remote_path,
                remote_filename
            )

            logger.info(f"Successfully uploaded {remote_filename} to {remote_path}")
            return result

        except Exception as e:
            logger.error(f"Failed to upload content to {remote_path}/{remote_filename}: {e}")
            raise Exception(f"FTP upload failed: {str(e)}")

    async def upload_file(self, local_path: str, remote_path: str, remote_filename: str) -> Dict[str, Any]:
        """
        Upload a file to the FTP server (async wrapper).

        Args:
            local_path: Path to the local file to upload
            remote_path: Remote directory path (e.g., '/ENTR/Orders')
            remote_filename: Name for the file on the remote server

        Returns:
            Dict containing upload result information

        Raises:
            Exception: If upload fails
        """
        # Read file content and use upload_content
        with open(local_path, 'r', encoding='utf-8') as f:
            content = f.read()

        return await self.upload_content(content, remote_path, remote_filename)

    def _delete_file_sync(self, remote_path: str) -> bool:
        """Synchronous FTP file deletion using ftplib."""
        hostname = self._get_hostname()
        ftp = None

        try:
            # Connect to FTP server
            ftp = FTP()
            ftp.connect(hostname, self.settings.SMURFIT_FTP_PORT)
            ftp.login(self.settings.SMURFIT_FTP_USERNAME, self.settings.SMURFIT_FTP_PASSWORD)

            # Delete the file
            response = ftp.delete(remote_path)
            logger.debug(f"FTP DELE command response for {remote_path}: {response}")

            # Verify deletion by listing the directory
            # Extract directory and filename
            if '/' in remote_path:
                directory = '/'.join(remote_path.split('/')[:-1])
                filename = remote_path.split('/')[-1]
            else:
                directory = ''
                filename = remote_path

            # Change to directory and check if file still exists
            if directory:
                ftp.cwd(directory)

            remaining_files = ftp.nlst()
            if filename in remaining_files:
                logger.error(f"File {filename} still exists in directory after DELETE command - possible permissions issue")
                return False

            logger.debug(f"Verified deletion of {remote_path} - file not found in directory listing")
            return True

        except Exception as e:
            # Check if error is due to file not existing (which means deletion worked on a previous attempt)
            error_str = str(e).lower()
            if 'no such file' in error_str or '550' in error_str:
                logger.debug(f"File {remote_path} already deleted or doesn't exist: {e}")
                return True
            logger.error(f"FTP delete operation failed for {remote_path}: {e}")
            return False
        finally:
            if ftp:
                try:
                    ftp.quit()
                except:
                    pass

    async def delete_file(self, remote_path: str) -> bool:
        """
        Delete a file from the FTP server (async wrapper).

        Args:
            remote_path: Full path to the remote file (including filename)

        Returns:
            True if deletion was successful, False otherwise
        """
        hostname = self._get_hostname()
        logger.info(f"Deleting {remote_path} from FTP server {hostname}")

        try:
            # Run synchronous FTP operation in thread pool
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(_executor, self._delete_file_sync, remote_path)

            if result:
                logger.info(f"Successfully deleted {remote_path}")
            return result

        except Exception as e:
            logger.error(f"Failed to delete {remote_path}: {e}")
            return False

    @staticmethod
    def parse_timestamp_from_filename(filename: str) -> Optional[str]:
        """
        Parse timestamp from filename following the pattern: 0610__<TableName>__<YYYYMMDDHHMMSS>.csv

        Args:
            filename: The filename to parse

        Returns:
            Timestamp string (YYYYMMDDHHMMSS) or None if not found
        """
        try:
            # Split by __ and get the last part
            parts = filename.split("__")
            if len(parts) >= 3:
                # Last part should be like "20250911100214.csv"
                timestamp_part = parts[-1].replace(".csv", "")
                # Validate it's 14 digits
                if len(timestamp_part) == 14 and timestamp_part.isdigit():
                    return timestamp_part
        except Exception as e:
            logger.debug(f"Could not parse timestamp from filename '{filename}': {e}")

        return None

    @staticmethod
    def extract_table_name_from_filename(filename: str) -> Optional[str]:
        """
        Extract table name from filename following the pattern: 0610__<TableName>__<YYYYMMDDHHMMSS>.csv

        Args:
            filename: The filename to parse

        Returns:
            Table name or None if not found
        """
        try:
            parts = filename.split("__")
            if len(parts) >= 3:
                return parts[1]
        except Exception as e:
            logger.debug(f"Could not parse table name from filename '{filename}': {e}")

        return None
