import httpx
from enum import Enum
from .retry import retry
from base64 import b64encode
from typing import Iterable



class Entity(Enum):
    CUSTOMER = "counterparty"
    PRODUCT = "product"
    VARIANT = "variant"
    BUNDLE = "bundle"
    SUPPLIER = "supplier"
    DEMAND = "demand"
    SUPPLY = "supply"
    INVOICE_IN = "invoicein"
    INVOICE_OUT = "invoiceout"
    PAYMENT_IN = "paymentin"
    PAYMENT_OUT = "paymentout"
    PURCHASE_ORDER = "purchaseorder"
    SALES_ORDER = "customerorder"
    PURCHASE_RETURN = "purchasereturn"
    SALES_RETURN = "salesreturn"
    LOSS = "loss"
    INVENTORY = "inventory"
    MOVE = "move"
    RETAIL_DEMAND = "retaildemand"
    RETAIL_SALES_RETURN = "retailsalesreturn"
    RETAIL_SHIFT = "retailshift"
    RETAIL_STORE = "retailstore"
    PROJECT = "project"
    STORE = "store"
    CONSIGNMENT = "consignment"
    TASK = "task"
    PRODUCT_FOLDER = "productfolder"
    CASH_IN = "cashin"
    CASH_OUT = "cashout"
    CASHINTAKE = "cashintake"
    DISASSEMBLY = "processing"
    ASSEMBLY = "processingorder"
    PROCESSING_PLAN = "processingplan"

    @property
    def endpoint(self) -> str:
        return f"/entity/{self.value}"
    




class APIClient:

    _DEFAULT_LIMITS = httpx.Limits(max_connections=10, max_keepalive_connections=5, keepalive_expiry=30.0)
    _BASE_URL = "https://api.moysklad.ru/api/remap/1.2"
    def __init__(
            self, timeout: float=15.0,
            base_url: str=_BASE_URL,
            limits: httpx.Limits=_DEFAULT_LIMITS):
        
        self.__client = httpx.AsyncClient(
            base_url=base_url,
            limits=limits,
            timeout=timeout,
            headers={"Accept-Encoding": "gzip"},
            follow_redirects=True
            )



    def _build_entity_url(self, entity_name: str | Entity, resource_id: str | None=None)-> str:
        try:
            entity = Entity(entity_name).endpoint if isinstance(entity_name, str) else entity_name.endpoint
        except ValueError:
            raise ValueError(f"{entity_name} is not valid Entity")
        
        api_url = entity + f"/{resource_id}" if resource_id else entity
        return api_url
    
    def _get_headers(self, api_key: str)->dict:
        if ":" in api_key:
            return {"Authorization": f"Basic {b64encode(api_key.encode()).decode()}"}
        return {"Authorization": f"Bearer {api_key}"}

    
    @retry
    async def _request(
            self,
            method: str, 
            api_key: str,
            entity_name: str | Entity,
            resource_id: None | str=None,
            params: dict | None=None,
            payload: dict | None=None)-> httpx.Response:

        api_url = self._build_entity_url(entity_name, resource_id)
        headers = self._get_headers(api_key)
        
        request = self.__client.request(
            method=method,
            url=api_url,
            headers=headers,
            params=params,
            json=payload


        )

        response = await request
        return response
        


    
    async def get(
                self,
                entity_name: str | Entity,
                api_key: str,
                resource_id: None | str=None, 
                params: dict | None = None)-> httpx.Response:
        response = await self._request(
            method='GET', 
            api_key=api_key, 
            entity_name=entity_name, 
            resource_id=resource_id, 
            params=params
            )
        return response
        
    async def post(
            self,
            entity_name: str | Entity,
            api_key: str,
            payload: dict | None=None,
            resource_id: None | str=None, 
            params: dict | None = None)-> httpx.Response:
        
        response = await self._request(
            method="POST", 
            api_key=api_key, 
            entity_name=entity_name, 
            resource_id=resource_id, 
            payload=payload,
            params=params
            )
        return response
    
    async def put(
            self,
            entity_name: str | Entity,
            api_key: str,
            payload: dict | None=None,
            resource_id: None | str=None, 
            params: dict | None = None)-> httpx.Response:
        
        response = await self._request(
            method="PUT", 
            api_key=api_key, 
            entity_name=entity_name, 
            resource_id=resource_id, 
            payload=payload,
            params=params
            )
        return response
    
    async def delete(
            self,
            entity_name: str | Entity,
            api_key: str,
            resource_id: None | str=None, 
            params: dict | None = None)-> httpx.Response:
        
        response = await self._request(
            method="DELETE", 
            api_key=api_key,
            entity_name=entity_name,
            resource_id=resource_id, 
            params=params
            )
        return response


    def __str__(self)->str:
        return f"{self.__class__.__name__}"

    async def aclose(self):
        await self.__client.aclose()

    async def __aenter__(self):
        return self
    
    async def __aexit__(self, exc_type, exc, tb):
        await self.__client.aclose()
    

