"""
Cleanup and Maintenance Worker Tasks
Handles session cleanup, log rotation, and system health checks
"""
import logging
from datetime import datetime, timedelta
from typing import Optional, Dict, Any

from celery import shared_task
from sqlalchemy import select, and_, delete

from app.workers.celery_app import celery
from app.database import get_sync_session
from app.models.user import UserSession, AuditLog
from app.models.webhook import WebhookDeliveryLog
from app.models.billing import UsageLog

logger = logging.getLogger(__name__)


@celery.task(
    bind=True,
    name="app.workers.tasks.cleanup_tasks.cleanup_expired_sessions",
    max_retries=2,
    queue="low_priority"
)
def cleanup_expired_sessions(self) -> Dict[str, Any]:
    """
    Clean up expired user sessions
    """
    try:
        logger.info("Cleaning up expired sessions")

        with get_sync_session() as session:
            # Find and delete expired sessions
            now = datetime.utcnow()

            result = session.execute(
                delete(UserSession)
                .where(UserSession.expires_at < now)
            )

            deleted_count = result.rowcount
            session.commit()

            logger.info(f"Deleted {deleted_count} expired sessions")

            return {
                "success": True,
                "deleted_sessions": deleted_count
            }

    except Exception as e:
        logger.error(f"Error cleaning up sessions: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.cleanup_tasks.cleanup_old_logs",
    max_retries=2,
    queue="low_priority"
)
def cleanup_old_logs(
    self,
    audit_log_retention_days: int = 90,
    webhook_log_retention_days: int = 30,
    usage_log_retention_days: int = 365
) -> Dict[str, Any]:
    """
    Clean up old log entries

    Args:
        audit_log_retention_days: Days to retain audit logs
        webhook_log_retention_days: Days to retain webhook delivery logs
        usage_log_retention_days: Days to retain detailed usage logs
    """
    try:
        logger.info("Cleaning up old logs")

        results = {
            "audit_logs_deleted": 0,
            "webhook_logs_deleted": 0,
            "usage_logs_deleted": 0
        }

        with get_sync_session() as session:
            now = datetime.utcnow()

            # Clean audit logs
            audit_cutoff = now - timedelta(days=audit_log_retention_days)
            result = session.execute(
                delete(AuditLog)
                .where(AuditLog.created_at < audit_cutoff)
            )
            results["audit_logs_deleted"] = result.rowcount

            # Clean webhook delivery logs
            webhook_cutoff = now - timedelta(days=webhook_log_retention_days)
            result = session.execute(
                delete(WebhookDeliveryLog)
                .where(WebhookDeliveryLog.created_at < webhook_cutoff)
            )
            results["webhook_logs_deleted"] = result.rowcount

            # Clean usage logs (keep summaries, remove detailed logs)
            usage_cutoff = now - timedelta(days=usage_log_retention_days)
            result = session.execute(
                delete(UsageLog)
                .where(UsageLog.created_at < usage_cutoff)
            )
            results["usage_logs_deleted"] = result.rowcount

            session.commit()

            logger.info(f"Log cleanup complete: {results}")

            return {
                "success": True,
                **results
            }

    except Exception as e:
        logger.error(f"Error cleaning up logs: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.cleanup_tasks.cleanup_temp_files",
    max_retries=2,
    queue="low_priority"
)
def cleanup_temp_files(self, max_age_hours: int = 24) -> Dict[str, Any]:
    """
    Clean up temporary files

    Args:
        max_age_hours: Maximum age of temp files in hours
    """
    try:
        import os
        import shutil
        from app.config import settings

        logger.info("Cleaning up temporary files")

        results = {
            "files_deleted": 0,
            "directories_deleted": 0,
            "space_freed_mb": 0
        }

        temp_dir = os.path.join(settings.LOCAL_STORAGE_PATH, "temp")
        if not os.path.exists(temp_dir):
            return {"success": True, **results}

        cutoff_time = datetime.utcnow() - timedelta(hours=max_age_hours)

        for item in os.listdir(temp_dir):
            item_path = os.path.join(temp_dir, item)
            try:
                item_mtime = datetime.fromtimestamp(os.path.getmtime(item_path))

                if item_mtime < cutoff_time:
                    if os.path.isfile(item_path):
                        size = os.path.getsize(item_path)
                        os.remove(item_path)
                        results["files_deleted"] += 1
                        results["space_freed_mb"] += size / (1024 * 1024)
                    elif os.path.isdir(item_path):
                        size = sum(
                            os.path.getsize(os.path.join(dirpath, filename))
                            for dirpath, _, filenames in os.walk(item_path)
                            for filename in filenames
                        )
                        shutil.rmtree(item_path)
                        results["directories_deleted"] += 1
                        results["space_freed_mb"] += size / (1024 * 1024)

            except Exception as e:
                logger.warning(f"Error deleting {item_path}: {str(e)}")

        results["space_freed_mb"] = round(results["space_freed_mb"], 2)
        logger.info(f"Temp file cleanup complete: {results}")

        return {"success": True, **results}

    except Exception as e:
        logger.error(f"Error cleaning up temp files: {str(e)}")
        self.retry(exc=e)


@celery.task(
    bind=True,
    name="app.workers.tasks.cleanup_tasks.cleanup_orphaned_files",
    max_retries=2,
    queue="low_priority"
)
def cleanup_orphaned_files(self) -> Dict[str, Any]:
    """
    Clean up files that are no longer referenced in the database
    """
    try:
        import os
        from app.config import settings
        from app.models.file_upload import FileUpload

        logger.info("Cleaning up orphaned files")

        results = {
            "files_checked": 0,
            "orphans_deleted": 0,
            "space_freed_mb": 0
        }

        uploads_dir = settings.LOCAL_STORAGE_PATH
        if not os.path.exists(uploads_dir):
            return {"success": True, **results}

        with get_sync_session() as session:
            # Get all file paths from database
            db_files = session.execute(
                select(FileUpload.storage_path)
                .where(FileUpload.storage_path.isnot(None))
            ).scalars().all()

            db_file_set = set(db_files)

            # Walk through upload directory
            for root, dirs, files in os.walk(uploads_dir):
                for filename in files:
                    file_path = os.path.join(root, filename)
                    relative_path = os.path.relpath(file_path, uploads_dir)
                    results["files_checked"] += 1

                    # Skip temp directory
                    if relative_path.startswith("temp"):
                        continue

                    # Check if file is in database
                    if relative_path not in db_file_set:
                        try:
                            size = os.path.getsize(file_path)
                            os.remove(file_path)
                            results["orphans_deleted"] += 1
                            results["space_freed_mb"] += size / (1024 * 1024)
                        except Exception as e:
                            logger.warning(f"Error deleting orphan {file_path}: {str(e)}")

        results["space_freed_mb"] = round(results["space_freed_mb"], 2)
        logger.info(f"Orphan cleanup complete: {results}")

        return {"success": True, **results}

    except Exception as e:
        logger.error(f"Error cleaning up orphaned files: {str(e)}")
        self.retry(exc=e)


@celery.task(
    name="app.workers.tasks.cleanup_tasks.health_check",
    queue="default"
)
def health_check() -> Dict[str, Any]:
    """
    Perform system health check
    """
    try:
        import redis
        from app.config import settings

        logger.info("Running health check")

        results = {
            "timestamp": datetime.utcnow().isoformat(),
            "status": "healthy",
            "checks": {}
        }

        # Check database connection
        try:
            with get_sync_session() as session:
                session.execute(select(1))
            results["checks"]["database"] = {"status": "healthy"}
        except Exception as e:
            results["checks"]["database"] = {"status": "unhealthy", "error": str(e)}
            results["status"] = "degraded"

        # Check Redis connection
        try:
            r = redis.from_url(settings.REDIS_URL)
            r.ping()
            results["checks"]["redis"] = {"status": "healthy"}
        except Exception as e:
            results["checks"]["redis"] = {"status": "unhealthy", "error": str(e)}
            results["status"] = "degraded"

        # Check disk space
        try:
            import shutil
            from app.config import settings

            total, used, free = shutil.disk_usage(settings.LOCAL_STORAGE_PATH)
            free_percent = (free / total) * 100

            results["checks"]["disk"] = {
                "status": "healthy" if free_percent > 10 else "warning",
                "free_percent": round(free_percent, 2),
                "free_gb": round(free / (1024 ** 3), 2)
            }

            if free_percent < 5:
                results["checks"]["disk"]["status"] = "critical"
                results["status"] = "degraded"

        except Exception as e:
            results["checks"]["disk"] = {"status": "unknown", "error": str(e)}

        # Check memory usage
        try:
            import psutil
            memory = psutil.virtual_memory()
            results["checks"]["memory"] = {
                "status": "healthy" if memory.percent < 90 else "warning",
                "used_percent": memory.percent,
                "available_gb": round(memory.available / (1024 ** 3), 2)
            }

            if memory.percent > 95:
                results["checks"]["memory"]["status"] = "critical"
                results["status"] = "degraded"

        except ImportError:
            results["checks"]["memory"] = {"status": "unknown", "error": "psutil not installed"}

        # Log results
        if results["status"] == "healthy":
            logger.info(f"Health check passed: {results}")
        else:
            logger.warning(f"Health check issues: {results}")

        return results

    except Exception as e:
        logger.error(f"Health check failed: {str(e)}")
        return {
            "timestamp": datetime.utcnow().isoformat(),
            "status": "unhealthy",
            "error": str(e)
        }


@celery.task(
    bind=True,
    name="app.workers.tasks.cleanup_tasks.vacuum_database",
    max_retries=1,
    queue="low_priority"
)
def vacuum_database(self) -> Dict[str, Any]:
    """
    Run database vacuum/analyze for PostgreSQL
    """
    try:
        logger.info("Running database vacuum")

        from sqlalchemy import text

        with get_sync_session() as session:
            # Run VACUUM ANALYZE on key tables
            tables = [
                "usage_logs",
                "messages",
                "conversations",
                "audit_logs",
                "webhook_delivery_logs"
            ]

            results = {"tables_vacuumed": []}

            for table in tables:
                try:
                    session.execute(text(f"VACUUM ANALYZE {table}"))
                    results["tables_vacuumed"].append(table)
                except Exception as e:
                    logger.warning(f"Failed to vacuum {table}: {str(e)}")

            session.commit()

        logger.info(f"Database vacuum complete: {results}")

        return {"success": True, **results}

    except Exception as e:
        logger.error(f"Error running database vacuum: {str(e)}")
        self.retry(exc=e)


@celery.task(
    name="app.workers.tasks.cleanup_tasks.expire_team_invitations",
    queue="low_priority"
)
def expire_team_invitations() -> Dict[str, Any]:
    """
    Expire old team invitations
    """
    try:
        from app.models.client_settings import TeamInvitation

        logger.info("Expiring old team invitations")

        with get_sync_session() as session:
            now = datetime.utcnow()

            # Find pending invitations past expiry
            expired = session.execute(
                select(TeamInvitation)
                .where(
                    and_(
                        TeamInvitation.status == "pending",
                        TeamInvitation.expires_at < now
                    )
                )
            ).scalars().all()

            expired_count = 0
            for invitation in expired:
                invitation.status = "expired"
                expired_count += 1

            session.commit()

            logger.info(f"Expired {expired_count} team invitations")

            return {
                "success": True,
                "expired_count": expired_count
            }

    except Exception as e:
        logger.error(f"Error expiring team invitations: {str(e)}")
        return {"success": False, "error": str(e)}
