# -*- coding: utf-8 -*-
"""Cliente HTTP conservador compartilhado pelas rotinas Python do addon.

Mantém uma Session por thread/escopo para reaproveitar keep-alive sem misturar
workers. Não altera URL, protocolo, hostname, porta, SNI ou verificação TLS.
Retentativas são explícitas e limitadas a GETs com falha transitória.
"""
import threading
import time

try:
    import requests
except Exception:
    requests = None


TRANSIENT_HTTP_STATUS = frozenset((429, 500, 502, 503, 504))
_HTTP_LOCAL = threading.local()


def _sessions():
    sessions = getattr(_HTTP_LOCAL, 'sessions', None)
    if not isinstance(sessions, dict):
        sessions = {}
        _HTTP_LOCAL.sessions = sessions
    return sessions


def get_thread_session(scope='default', headers=None, pool_connections=8, pool_maxsize=8):
    """Retorna uma Session persistente e isolada na thread atual."""
    if requests is None:
        raise RuntimeError('requests indisponível no Kodi')
    scope = str(scope or 'default')
    sessions = _sessions()
    session = sessions.get(scope)
    if session is None:
        session = requests.Session()
        adapter = requests.adapters.HTTPAdapter(
            pool_connections=max(1, int(pool_connections or 1)),
            pool_maxsize=max(1, int(pool_maxsize or 1)),
            max_retries=0,
            pool_block=True,
        )
        session.mount('http://', adapter)
        session.mount('https://', adapter)
        sessions[scope] = session
    if isinstance(headers, dict) and headers:
        session.headers.update(headers)
    return session


def _response_status(response):
    try:
        return int(response.status_code)
    except Exception:
        return None


def _close_response(response):
    try:
        if response is not None:
            response.close()
    except Exception:
        pass


def _retry_delay(backoff, attempt, response=None):
    delay = max(0.2, float(backoff or 1.0)) * (2 ** max(0, int(attempt)))
    try:
        retry_after = (response.headers.get('Retry-After') or '').strip()
        if retry_after.isdigit():
            delay = max(delay, float(retry_after))
    except Exception:
        pass
    # A UI e o service não devem ficar presos a um Retry-After abusivo.
    return min(5.0, delay)


def _is_retryable_exception(exc):
    if requests is None:
        return False
    try:
        ssl_error = getattr(requests.exceptions, 'SSLError', ())
        if ssl_error and isinstance(exc, ssl_error):
            return False
    except Exception:
        pass
    try:
        return isinstance(exc, (requests.Timeout, requests.ConnectionError))
    except Exception:
        return False


def safe_get(url, session=None, retries=2, backoff=1.0, retry_statuses=None, **kwargs):
    """GET com pool e retentativas conservadoras, preservando a URL original."""
    if requests is None:
        raise RuntimeError('requests indisponível no Kodi')
    if session is None:
        session = get_thread_session()
    retries = max(0, int(retries or 0))
    statuses = set(retry_statuses or TRANSIENT_HTTP_STATUS)
    last_exc = None

    for attempt in range(retries + 1):
        response = None
        try:
            response = session.get(url, **kwargs)
            status = _response_status(response)
            if status in statuses and attempt < retries:
                delay = _retry_delay(backoff, attempt, response=response)
                _close_response(response)
                time.sleep(delay)
                continue
            response.raise_for_status()
            return response
        except Exception as exc:
            last_exc = exc
            status = _response_status(getattr(exc, 'response', None))
            retryable = status in statuses or _is_retryable_exception(exc)
            if retryable and attempt < retries:
                failed_response = getattr(exc, 'response', None) or response
                delay = _retry_delay(backoff, attempt, response=failed_response)
                _close_response(failed_response)
                time.sleep(delay)
                continue
            raise

    if last_exc is not None:
        raise last_exc
    raise requests.RequestException('Falha na requisição')


def close_thread_sessions(scope=None):
    """Fecha sessões criadas na thread atual; útil no encerramento do service."""
    sessions = _sessions()
    keys = [str(scope)] if scope is not None else list(sessions.keys())
    for key in keys:
        session = sessions.pop(key, None)
        try:
            if session is not None:
                session.close()
        except Exception:
            pass
