"""
Retry Manager

Core retry logic and cooldown management for download attempts.
Prevents endless retry loops and implements intelligent retry strategies.
"""

from dataclasses import dataclass
from datetime import datetime, timedelta, UTC
from typing import List, Optional, Tuple

from loguru import logger
from .failure_classifier import FailureClassifier
from .status_tracker import ResourceStatusTracker


@dataclass
class RetryDecision:
    """Decision about whether to retry a resource"""

    can_retry: bool
    reason: Optional[str] = None
    estimated_wait_time: Optional[timedelta] = None


class ResourceFilterResult:
    """Result of filtering a resource"""

    def __init__(
        self,
        resource_id: int,
        can_retry: bool,
        status: str,
        reason: str = "",
        estimated_wait: Optional[timedelta] = None,
    ):
        self.resource_id = resource_id
        self.can_retry = can_retry
        self.status = status
        self.reason = reason
        self.estimated_wait = estimated_wait


class FilterSummary:
    """Summary of filtering results"""

    def __init__(self):
        self.total_count = 0
        self.downloadable_count = 0
        self.permanently_failed_count = 0
        self.temporarily_failed_count = 0
        self.available_count = 0
        self.failure_type_counts = {}

    def add_result(self, result: ResourceFilterResult):
        """Add a filtering result to the summary"""
        self.total_count += 1

        if result.can_retry:
            self.downloadable_count += 1
        elif result.status == "permanently_failed":
            self.permanently_failed_count += 1
        elif result.status == "temporarily_failed":
            self.temporarily_failed_count += 1
        else:
            self.available_count += 1

    def to_dict(self) -> dict:
        """Convert to dictionary"""
        return {
            "total_count": self.total_count,
            "downloadable_count": self.downloadable_count,
            "permanently_failed_count": self.permanently_failed_count,
            "temporarily_failed_count": self.temporarily_failed_count,
            "available_count": self.available_count,
            "failure_type_counts": self.failure_type_counts,
        }


class RetryManager:
    """Manage retry logic and prevent endless loops"""

    def __init__(self, username: str, password: Optional[str] = None):
        """
        Initialize the retry manager.

        Args:
            username: Username for database access
            password: Optional password for encrypted database
        """
        self.username = username
        self.failure_classifier = FailureClassifier()
        self.status_tracker = ResourceStatusTracker(username, password)

        logger.info(f"Initialized for user: {username}")

    def should_retry_resource(self, resource_id: int) -> RetryDecision:
        """
        Determine if a resource should be retried based on history.

        Args:
            resource_id: Resource identifier

        Returns:
            RetryDecision with can_retry flag and reasoning
        """
        can_retry, reason = self.status_tracker.can_retry(resource_id)
        return RetryDecision(can_retry=can_retry, reason=reason)

    def record_attempt(
        self,
        resource_id: int,
        result: Tuple[bool, Optional[str]],
        status_code: Optional[int] = None,
        url: str = "",
        details: str = "",
        session=None,
    ) -> None:
        """
        Record a download attempt result.

        Args:
            resource_id: Resource identifier
            result: Tuple of (success, error_message)
            status_code: HTTP status code if available
            url: URL that was attempted
            details: Additional error details
            session: Optional database session to reuse
        """
        success, error_message = result

        if success:
            # Successful download
            self.status_tracker.mark_success(resource_id, session=session)
            logger.info(f"Resource {resource_id} marked as successful")
        else:
            # Failed download - classify the failure
            failure = self.failure_classifier.classify_failure(
                error_type=type(error_message).__name__
                if error_message
                else "unknown",
                status_code=status_code,
                url=url,
                details=details or (error_message or "Unknown error"),
            )

            self.status_tracker.mark_failure(
                resource_id, failure, session=session
            )
            logger.info(
                f"Resource {resource_id} marked as failed: {failure.error_type}"
            )

    def filter_resources(self, resources: List) -> List[ResourceFilterResult]:
        """
        Filter resources based on their retry eligibility.

        Args:
            resources: List of resources to filter

        Returns:
            List of ResourceFilterResult objects
        """
        results = []

        for resource in resources:
            if not hasattr(resource, "id"):
                # Skip resources without ID
                continue

            can_retry, reason = self.status_tracker.can_retry(resource.id)
            status = self._get_resource_status(can_retry, reason)

            # Get estimated wait time if temporarily failed
            estimated_wait = None
            if not can_retry and reason and "cooldown" in reason.lower():
                try:
                    status_info = self.status_tracker.get_resource_status(
                        resource.id
                    )
                    if status_info and status_info["retry_after_timestamp"]:
                        retry_time = datetime.fromisoformat(
                            status_info["retry_after_timestamp"]
                        )
                        estimated_wait = retry_time - datetime.now(UTC)
                except Exception as e:
                    logger.debug(f"Error calculating wait time: {e}")

            result = ResourceFilterResult(
                resource_id=resource.id,
                can_retry=can_retry,
                status=status,
                reason=reason or "",
                estimated_wait=estimated_wait,
            )
            results.append(result)

        logger.info(
            f"Filtered {len(results)} resources: "
            f"{sum(1 for r in results if r.can_retry)} downloadable, "
            f"{sum(1 for r in results if r.status == 'permanently_failed')} permanently failed"
        )

        return results

    def get_filter_summary(
        self, results: List[ResourceFilterResult]
    ) -> FilterSummary:
        """
        Generate a summary of filtering results.

        Args:
            results: List of ResourceFilterResult objects

        Returns:
            FilterSummary object with counts
        """
        summary = FilterSummary()
        for result in results:
            summary.add_result(result)
        return summary

    def _get_resource_status(
        self, can_retry: bool, reason: Optional[str]
    ) -> str:
        """Get status string based on retry decision"""
        if not can_retry:
            if reason and "permanently failed" in reason:
                return "permanently_failed"
            if reason and "cooldown" in reason:
                return "temporarily_failed"
            return "unavailable"
        return "available"

    def reset_daily_retry_counters(self) -> int:
        """
        Reset daily retry counters (call this at midnight).

        Returns:
            Number of resources that had their daily counter reset
        """
        with self.status_tracker._get_session() as session:
            # Reset all today_retry_count to 0
            from .models import ResourceDownloadStatus

            result = session.query(ResourceDownloadStatus).update(
                {"today_retry_count": 0, "updated_at": datetime.now(UTC)}
            )
            session.commit()
            logger.info(f"Reset daily retry counters for {result} resources")
            return result

    def clear_old_permanent_failures(self, days: int = 30) -> int:
        """
        Clear old permanent failure records.

        Args:
            days: Clear failures older than this many days

        Returns:
            Number of records cleared
        """
        return self.status_tracker.clear_permanent_failures(days)
