from datetime import datetime

import pkce
import pytz
import requests
from requests_oauthlib import OAuth2Session

from V2.functions.Applications.main import Application
from V2.functions.Etsy.Shops import getEtsyShop
from V2.functions.Shops.main import Shop
from V2.middlewares.auth import API_Error
from V2.Params import Params


def authUrl(params:Params) -> dict:
    enterpriseId = params.currentUser.enterpriseId
    app = Application.get("1"+enterpriseId)
    redirect_uri = "http://localhost:3000/shops/redirects/etsy" if params.hostname == "localhost" else f'https://{params.hostname}/shops/redirects/etsy'
    scope = ["listings_d", "listings_w", "listings_r", "transactions_r","transactions_w", "shops_r", "profile_r", "email_r"]
    oauth = OAuth2Session(app.apiKey, redirect_uri=redirect_uri, scope=scope)
    code_verifier, code_challenge = pkce.generate_pkce_pair()
    url, state  = oauth.authorization_url("https://www.etsy.com/oauth/connect", code_challenge=code_challenge, code_challenge_method="S256")
    return dict(url=url, state=state, code_verifier=code_verifier, code_challenge=code_challenge)
    
def authToken(params:Params) -> Shop:
    enterpriseId = params.currentUser.enterpriseId
    app = Application.get("1"+enterpriseId)
    if app:
        oauth = OAuth2Session(
            app.apiKey,
            redirect_uri = "http://localhost:3000/shops/redirects/etsy" if params.hostname == "localhost" else f'https://{params.hostname}/shops/redirects/etsy'
        )
        token = oauth.fetch_token("https://api.etsy.com/v3/public/oauth/token", 
            code=params.args.get('code'),
            code_verifier=params.args.get('code_verifier'),
            include_client_id=True,
            headers={
             "x-api-key": app.apiKey,
            }
        )
        
        if not token: raise API_Error("Cannot get token.")
        oauth.token = token
        etsyShop = getEtsyShop(oauth)
        shop = Shop(
            name=etsyShop.get("shop_name"),
            platformShopId=etsyShop.get("shop_id"),
            platformId=app.platformId,
            platformName=app.platformName,
            appId=app.id,
            uid=params.currentUser.uid,
            enterpriseId=app.enterpriseId,
            url=etsyShop.get("url"),
            accessToken=token.get('access_token'),
            refreshToken=token.get('refresh_token'),
            apiVersion="v3",
            tokenType=token.get("token_type"),
            expiresAt=int(datetime.now(pytz.utc).timestamp()+token.get("expires_in")),
        ).save()
        return shop.to_dict()
    raise API_Error("App not found.")

class EtsyClient:
    def __init__(self,apiKey:str,shop:Shop) -> None:
        self.apiKey = apiKey
        self.shop = shop
        self.auth = OAuth2Session(
            apiKey,
            token=dict(
                access_token= shop.accessToken,
                token_type=shop.tokenType,
            ),
        )
        if self.shop.expiresAt:
            if self.shop.expiresAt < datetime.now(pytz.utc).timestamp(): self.refreshToken()
        else: print(self.shop)

    def refreshToken(self):
        res = self.auth.refresh_token("https://api.etsy.com/v3/public/oauth/token", 
            self.shop.refreshToken, 
            client_id=self.apiKey,
            headers={"x-api-key": self.apiKey}
        )
        self.shop.accessToken = res.get("access_token")
        self.shop.tokenType = res.get("token_type")
        self.shop.refreshToken = res.get("refresh_token")
        self.shop.expiresAt = int(res.get("expires_in") + datetime.now(pytz.utc).timestamp())
        self.shop.timezone = pytz.utc.tzname(dt=datetime.now())
        self.shop.update()

    def get(self, url:str, params:dict={}):
        return self.auth.get(url,params=params, headers={"x-api-key": self.apiKey})

    def post(self, url:str, data:dict={},json:dict=None,headers:dict={"Content-Type": "application/json"}, files=None):
        if files:return self.auth.post(url,files=files, headers={"x-api-key": self.apiKey, ** headers})
        if data: return self.auth.post(url,data=data, headers={"x-api-key": self.apiKey, ** headers})
        return self.auth.post(url,data=data,json=json,headers={"x-api-key": self.apiKey, ** headers})
    
    def upload(self, url, files, data):
        return self.auth.post(url, files=files, data=data, headers={"x-api-key": self.apiKey})
    
    def put(self, url:str, data:dict={},json:dict=None, headers:dict={"Content-Type": "application/json"}):
        return self.auth.put(url,data=data,json=json, headers={"x-api-key": self.apiKey,**headers})
    
    def delete(self, url:str):
        return self.auth.delete(url, headers={"x-api-key": self.apiKey})

def exchangeTokens(params:Params):
    shop = Shop.get(params.args.get("id"))
    url = "https://api.etsy.com/v3/public/oauth/token"
    app = Application.get("1"+shop.enterpriseId)
    print(app.id, params)
    if app:
        oauth = OAuth2Session(
            app.apiKey,
            redirect_uri = "http://localhost:3000/shops/redirects/etsy"
        )
        token = requests.post(url, 
        data=dict(
            legacy_token=params.args.get("legacy_token"),
            client_id=app.apiKey,
            grant_type="token_exchange"
         ),
            headers={
             "x-api-key": app.apiKey,
            }
        )
        print(token.text)
        if  token.status_code != 200: raise API_Error("Cannot get token.")
        oauth.token = dict(token.json())
        etsyShop = getEtsyShop(oauth)
        shop = Shop(
            name=etsyShop.get("shop_name"),
            platformShopId=etsyShop.get("shop_id"),
            platformId=app.platformId,
            platformName=app.platformName,
            appId=app.id,
            uid=shop.uid,
            enterpriseId=app.enterpriseId,
            url=etsyShop.get("url"),
            accessToken=oauth.token.get('access_token'),
            refreshToken=oauth.token.get('refresh_token'),
            apiVersion="v3",
            tokenType=oauth.token.get("token_type"),
            expiresAt=oauth.token.get("expires_in"),
        ).save(oauthToken=None, oauthTokenSecret=None)
        return shop.to_dict()
    raise API_Error("App not found.")
