"""
Resource Status Tracker

Tracks download attempts, failures, and cooldowns in the database.
Provides persistent storage for failure classifications and retry eligibility.
"""

from datetime import datetime, timedelta, UTC
from typing import Optional, Dict, Any

from loguru import logger
from sqlalchemy.orm import sessionmaker, Session

from .models import ResourceDownloadStatus, Base
from .failure_classifier import BaseFailure


class ResourceStatusTracker:
    """Track download attempts, failures, and cooldowns in database"""

    def __init__(self, username: str, password: Optional[str] = None):
        """
        Initialize the status tracker for a user.

        Args:
            username: Username for database access
            password: Optional password for encrypted database
        """
        self.username = username
        self.password = password

        # Use the global db_manager singleton to share cached connections
        from ...database.encrypted_db import db_manager

        self.db_manager = db_manager
        self.engine = db_manager.open_user_database(username, password)
        self.Session = sessionmaker(bind=self.engine)

        # Create tables if they don't exist
        Base.metadata.create_all(self.engine)
        logger.info(
            f"[STATUS_TRACKER] Initialized for user: {username} with encrypted database"
        )

    def _get_session(self) -> Session:
        """Get a database session"""
        return self.Session()

    def mark_failure(
        self,
        resource_id: int,
        failure: BaseFailure,
        session: Optional[Session] = None,
    ) -> None:
        """
        Mark a resource as failed with classification.

        Args:
            resource_id: Resource identifier
            failure: Classified failure object
            session: Optional existing database session to reuse
        """
        # Use provided session or create new one
        should_close = session is None
        if session is None:
            session = self._get_session()

        try:
            # Get or create status record
            status = (
                session.query(ResourceDownloadStatus)
                .filter_by(resource_id=resource_id)
                .first()
            )

            if not status:
                status = ResourceDownloadStatus(resource_id=resource_id)
                session.add(status)

            # Update status information
            if failure.is_permanent():
                status.status = "permanently_failed"
                status.retry_after_timestamp = None
                status.failure_type = failure.error_type
                status.failure_message = failure.message
                status.permanent_failure_at = datetime.now(UTC)
                logger.info(
                    f"[STATUS_TRACKER] Marked resource {resource_id} as permanently failed: {failure.error_type}"
                )
            else:
                status.status = "temporarily_failed"
                status.retry_after_timestamp = (
                    failure.created_at + failure.retry_after
                )
                status.failure_type = failure.error_type
                status.failure_message = failure.message
                logger.info(
                    f"[STATUS_TRACKER] Marked resource {resource_id} as temporarily failed: {failure.error_type}, retry after: {failure.retry_after}"
                )

            # Update retry statistics
            # Ensure total_retry_count is initialized (handle None from legacy data)
            if status.total_retry_count is None:
                status.total_retry_count = 0
            status.total_retry_count += 1
            status.last_attempt_at = datetime.now(UTC)

            # Check if retry is today
            today = datetime.now(UTC).date()
            last_attempt = (
                status.last_attempt_at.date()
                if status.last_attempt_at
                else None
            )
            if last_attempt == today:
                # Ensure today_retry_count is initialized (handle None from legacy data)
                if status.today_retry_count is None:
                    status.today_retry_count = 0
                status.today_retry_count += 1
            else:
                status.today_retry_count = 1

            # Only commit if we created the session
            if should_close:
                session.commit()
            logger.debug(
                f"[STATUS_TRACKER] Updated failure status for resource {resource_id}"
            )
        finally:
            if should_close:
                session.close()

    def mark_success(
        self, resource_id: int, session: Optional[Session] = None
    ) -> None:
        """
        Mark a resource as successfully downloaded.

        Args:
            resource_id: Resource identifier
            session: Optional existing database session to reuse
        """
        # Use provided session or create new one
        should_close = session is None
        if session is None:
            session = self._get_session()

        try:
            status = (
                session.query(ResourceDownloadStatus)
                .filter_by(resource_id=resource_id)
                .first()
            )

            if status:
                status.status = "completed"
                status.failure_type = None
                status.failure_message = None
                status.retry_after_timestamp = None
                status.updated_at = datetime.now(UTC)
                logger.info(
                    f"[STATUS_TRACKER] Marked resource {resource_id} as successfully completed"
                )

            # Only commit if we created the session
            if should_close:
                session.commit()
        finally:
            if should_close:
                session.close()

    def can_retry(self, resource_id: int) -> tuple[bool, Optional[str]]:
        """
        Check if a resource can be retried right now.

        Args:
            resource_id: Resource identifier

        Returns:
            Tuple of (can_retry, reason_if_not)
        """
        with self._get_session() as session:
            status = (
                session.query(ResourceDownloadStatus)
                .filter_by(resource_id=resource_id)
                .first()
            )

            if not status:
                # No status record, can retry
                return True, None

            # Check if permanently failed
            if status.status == "permanently_failed":
                return (
                    False,
                    f"Permanently failed: {status.failure_message or status.failure_type}",
                )

            # Check if temporarily failed and cooldown not expired
            if (
                status.status == "temporarily_failed"
                and status.retry_after_timestamp
            ):
                # Ensure retry_after_timestamp is timezone-aware (handle legacy data)
                retry_timestamp = status.retry_after_timestamp
                if retry_timestamp.tzinfo is None:
                    # Assume UTC for naive timestamps
                    retry_timestamp = retry_timestamp.replace(tzinfo=UTC)

                if datetime.now(UTC) < retry_timestamp:
                    return (
                        False,
                        f"Cooldown active, retry available at {retry_timestamp.strftime('%Y-%m-%d %H:%M:%S')}",
                    )

            # Check daily retry limit (max 3 retries per day)
            if status.today_retry_count >= 3:
                return (
                    False,
                    f"Daily retry limit exceeded ({status.today_retry_count}/3). Retry available tomorrow.",
                )

            # Can retry
            return True, None

    def get_resource_status(self, resource_id: int) -> Optional[Dict[str, Any]]:
        """
        Get the current status of a resource.

        Args:
            resource_id: Resource identifier

        Returns:
            Status information dictionary or None if not found
        """
        with self._get_session() as session:
            status = (
                session.query(ResourceDownloadStatus)
                .filter_by(resource_id=resource_id)
                .first()
            )

            if not status:
                return None

            return {
                "resource_id": status.resource_id,
                "status": status.status,
                "failure_type": status.failure_type,
                "failure_message": status.failure_message,
                "retry_after_timestamp": status.retry_after_timestamp.isoformat()
                if status.retry_after_timestamp
                else None,
                "last_attempt_at": status.last_attempt_at.isoformat()
                if status.last_attempt_at
                else None,
                "total_retry_count": status.total_retry_count,
                "today_retry_count": status.today_retry_count,
                "created_at": status.created_at.isoformat(),
                "updated_at": status.updated_at.isoformat(),
            }

    def get_failed_resources_count(self) -> Dict[str, int]:
        """
        Get counts of resources by failure type.

        Returns:
            Dictionary mapping failure types to counts
        """
        with self._get_session() as session:
            failed_resources = (
                session.query(ResourceDownloadStatus)
                .filter(
                    ResourceDownloadStatus.status.in_(
                        ["temporarily_failed", "permanently_failed"]
                    )
                )
                .all()
            )

            counts = {}
            for resource in failed_resources:
                failure_type = resource.failure_type or "unknown"
                counts[failure_type] = counts.get(failure_type, 0) + 1

            return counts

    def clear_permanent_failures(self, older_than_days: int = 30) -> int:
        """
        Clear permanent failure statuses for old records.

        Args:
            older_than_days: Clear failures older than this many days

        Returns:
            Number of records cleared
        """
        cutoff_date = datetime.now(UTC) - timedelta(days=older_than_days)

        with self._get_session() as session:
            old_failures = (
                session.query(ResourceDownloadStatus)
                .filter(
                    ResourceDownloadStatus.status == "permanently_failed",
                    ResourceDownloadStatus.created_at < cutoff_date,
                )
                .all()
            )

            count = len(old_failures)
            for failure in old_failures:
                failure.status = "available"
                failure.failure_type = None
                failure.failure_message = None
                failure.retry_after_timestamp = None
                failure.permanent_failure_at = None
                failure.total_retry_count = 0
                failure.today_retry_count = 0
                failure.updated_at = datetime.now(UTC)

            session.commit()
            logger.info(
                f"[STATUS_TRACKER] Cleared {count} old permanent failure records"
            )
            return count
