import time
from functools import wraps

from .backoff import exponential_backoff
from .policies import DEFAULT_POLICIES


def retry(
    policy="default",
    delay=1,
    retry_status=None,
    max_retries=None,
    allowed_methods=None,
    on_retry=None,
    on_failure=None,
    on_success=None,
):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):

            policy_config = DEFAULT_POLICIES.get(policy, DEFAULT_POLICIES["default"])

            final_retry_status = set(retry_status) if retry_status else policy_config["retry_status"]
            final_max_retries = max_retries if max_retries else policy_config["max_retries"]
            final_allowed_methods = (
                set(allowed_methods) if allowed_methods else {"GET", "HEAD", "OPTIONS"}
            )

            for attempt in range(final_max_retries):
                try:
                    print(f"Attempt {attempt + 1}")

                    response = func(*args, **kwargs)

                    if hasattr(response, "status_code") and response.status_code in final_retry_status:

                        method = None
                        if hasattr(response, "request"):
                            method = response.request.method

                        if method and method.upper() not in final_allowed_methods:
                            print(f"Method {method} not allowed for retry.")
                            return response

                        print(f"Retryable status: {response.status_code}")

                        if attempt == final_max_retries - 1:
                            if on_failure:
                                on_failure({
                                    "attempts": final_max_retries,
                                    "status": response.status_code,
                                })

                            print("Max retries reached.")
                            return response

                        retry_after = None
                        if hasattr(response, "headers"):
                            retry_after = response.headers.get("Retry-After")

                        wait_time = int(retry_after) if retry_after else exponential_backoff(attempt, delay)

                        # 🔥 RETRY HOOK
                        if on_retry:
                            on_retry({
                                "attempt": attempt + 1,
                                "wait": wait_time,
                                "status": response.status_code,
                                "method": method,
                            })

                        print(f"Waiting {wait_time}s before retry...\n")
                        time.sleep(wait_time)
                        continue

                    # 🔥 SUCCESS HOOK
                    if on_success:
                        on_success({
                            "attempt": attempt + 1,
                            "status": getattr(response, "status_code", None),
                        })

                    return response

                except Exception as e:
                    print(f"Exception: {e}")

                    if attempt == final_max_retries - 1:
                        if on_failure:
                            on_failure({"attempts": final_max_retries})
                        print("Max retries reached.")
                        raise

                    wait_time = exponential_backoff(attempt, delay)

                    if on_retry:
                        on_retry({
                            "attempt": attempt + 1,
                            "wait": wait_time,
                            "exception": str(e),
                        })

                    print(f"Waiting {wait_time}s before retry...\n")
                    time.sleep(wait_time)

            raise Exception("Max retries exceeded")

        return wrapper
    return decorator
