"""Retry logic with IP rotation and fast failover."""

import time
import uuid
import logging

logger = logging.getLogger("iploop")

RETRYABLE_STATUS = {403, 407, 429, 500, 502, 503, 504}


def should_retry(exc=None, status_code=None):
    if status_code and status_code in RETRYABLE_STATUS:
        return True
    if exc:
        from requests.exceptions import (ConnectionError, Timeout, ProxyError)
        return isinstance(exc, (ConnectionError, Timeout, ProxyError))
    return False


def new_session_id():
    return uuid.uuid4().hex[:16]


def retry_request(func, retries=3, delay=0.5, max_delay=5.0):
    """
    Execute func with retries. func receives attempt number.
    
    Fast retry strategy:
    - Attempt 1: immediate
    - Attempt 2: 0.5s delay + new session ID (rotate IP)
    - Attempt 3: 1.0s delay + new session ID
    
    Total max wait: ~1.5s for retries (not 90s)
    """
    last_exc = None
    last_resp = None
    
    for attempt in range(retries):
        try:
            resp = func(attempt)
            if hasattr(resp, 'status_code'):
                if resp.status_code == 407:
                    # Auth failed — don't retry, it won't help
                    logger.debug("Auth failed (407), not retrying")
                    return resp
                if should_retry(status_code=resp.status_code):
                    last_resp = resp
                    logger.debug("Retry %d/%d: status %d", attempt + 1, retries, resp.status_code)
                    if attempt < retries - 1:
                        wait = min(delay * (attempt + 1), max_delay)
                        time.sleep(wait)
                        continue
                    return resp  # Return last response even if bad
            return resp
        except Exception as e:
            last_exc = e
            if should_retry(exc=e) and attempt < retries - 1:
                wait = min(delay * (attempt + 1), max_delay)
                logger.debug("Retry %d/%d (%.1fs): %s", attempt + 1, retries, wait, type(e).__name__)
                time.sleep(wait)
            elif attempt == retries - 1:
                break
            else:
                raise
    
    # If we have a response (even bad status), return it instead of raising
    if last_resp is not None:
        return last_resp
    
    if last_exc:
        raise last_exc
    raise RuntimeError("retry_request: no response and no exception")
