import pandas as pd
import jwt
import uuid
import time
import requests
import json
import hashlib
from urllib.parse import urlencode


class HTS:
    """빗썸 Open API 래퍼 (시세 조회, 잔고, 주문)."""

    bithumb = None
    accessKey = ""
    secretKey = ""
    apiUrl = ""

    def __init__(self):
        from config import BITHUMB_ACCESS_KEY, BITHUMB_API_URL, BITHUMB_SECRET_KEY

        self.bithumb = None
        self.accessKey = BITHUMB_ACCESS_KEY
        self.secretKey = BITHUMB_SECRET_KEY
        self.apiUrl = BITHUMB_API_URL.rstrip("/")

    def append(self, stock, df=None, data_1=None):
        if df is not None:
            for i in range(len(df)):
                stock['PRICE'].append(
                    {
                        "ymd": df.index[i],
                        "close": df['close'].iloc[i],
                        "diff": 0,
                        "open": df['open'].iloc[i],
                        "high": df['high'].iloc[i],
                        "low": df['low'].iloc[i],
                        "volume": df['volume'].iloc[i],
                        "avg5": -1, "avg20": -1, "avg60": -1, "avg120": -1, "avg240": -1, "avg480": -1,
                        "bolingerband_upper": -1, "bolingerband_lower": -1, "bolingerband_middle": -1, "bolingerband_bwi": -1,
                        "ichimokucloud_changeLine": -1, "ichimokucloud_baseLine": -1, "ichimokucloud_leadingSpan1": -1, "ichimokucloud_leadingSpan2": -1,
                        "stochastic_fast_k_1": -1, "stochastic_slow_k_1": -1, "stochastic_slow_d_1": -1,
                        "stochastic_fast_k_2": -1, "stochastic_slow_k_2": -1, "stochastic_slow_d_2": -1,
                        "stochastic_fast_k_3": -1, "stochastic_slow_k_3": -1, "stochastic_slow_d_3": -1,
                        "rsi": -1, "rsis": -1,
                        "macd": -1, "macds": -1, "macdo": -1, "nor_macd": -1, "nor_macds": -1, "nor_macdo": -1,
                    })

        if data_1 is not None:
            stock['PRICE'].append(
                {
                    "ymd": data_1.index[-1],
                    "close": data_1['close'].iloc[-1],
                    "diff": 0,
                    "open": data_1['open'].iloc[-1],
                    "high": data_1['high'].iloc[-1],
                    "low": data_1['low'].iloc[-1],
                    "volume": data_1['volume'].iloc[-1],
                    "avg5": -1, "avg20": -1, "avg60": -1, "avg120": -1, "avg240": -1, "avg480": -1,
                    "bolingerband_upper": -1, "bolingerband_lower": -1, "bolingerband_middle": -1, "bolingerband_bwi": -1, "bolingerband_nor_bwi": -1,
                    "envelope_upper": -1, "envelope_lower": -1, "envelope_middle": -1,
                    "ichimokucloud_changeLine": -1, "ichimokucloud_baseLine": -1, "ichimokucloud_leadingSpan1": -1,
                    "ichimokucloud_leadingSpan2": -1,
                    "stochastic_fast_k_1": -1, "stochastic_slow_k_1": -1, "stochastic_slow_d_1": -1,
                    "stochastic_fast_k_2": -1, "stochastic_slow_k_2": -1, "stochastic_slow_d_2": -1,
                    "stochastic_fast_k_3": -1, "stochastic_slow_k_3": -1, "stochastic_slow_d_3": -1,
                    "rsi": -1, "rsis": -1,
                    "macd": -1, "macds": -1, "macdo": -1, "nor_macd": -1, "nor_macds": -1, "nor_macdo": -1,
                })
        return

    def getCoinRawData(self, ticker_code, minute=None, day=False, week=False, month=False, to=None, endpoint='/v1/candles'):
        url = None
        if minute == 0:
            # 현재가 정보
            url = (self.apiUrl + "/v1/ticker?markets=KRW-{}").format(ticker_code)

            headers = {"accept": "application/json"}
            response = requests.get(url, headers=headers)
            json_data = json.loads(response.text)
            df_temp = pd.DataFrame(json_data)
            if 'trade_date_kst' not in df_temp or 'trade_time_kst' not in df_temp:
                return None
            df = pd.DataFrame()
            df['datetime'] = pd.to_datetime(df_temp['trade_date_kst'], format='%Y-%m-%dT%H:%M:%S')
            df['open'] = df_temp['opening_price']
            df['close'] = df_temp['trade_price']
            df['high'] = df_temp['high_price']
            df['low'] = df_temp['low_price']
            df['volume'] = df_temp['trade_volume']
            df = df.set_index('datetime')
            df = df.astype(float)
            df["datetime"] = df.index
        else:
            # 분봉
            if minute is not None and minute in {1, 3, 5, 10, 15, 30, 60, 240}:
                if to is None:
                    url = (self.apiUrl + endpoint + "/minutes/{}?market=KRW-{}&count=3000").format(minute, ticker_code)
                else:
                    url = (self.apiUrl + endpoint + "/minutes/{}?market=KRW-{}&count=3000&to={}").format(minute, ticker_code, to)
            if day:
                if to is None:
                    url = (self.apiUrl + endpoint + "/days?market=KRW-{}&count=3000").format(ticker_code)
                else:
                    url = (self.apiUrl + endpoint + "/days?market=KRW-{}&count=3000&to={}").format(ticker_code, to)
            if week:
                if to is None:
                    url = (self.apiUrl + endpoint + "/weeks?market=KRW-{}&count=3000").format(ticker_code)
                else:
                    url = (self.apiUrl + endpoint + "/weeks?market=KRW-{}&count=3000&to={}").format(ticker_code, to)
            if month:
                if to is None:
                    url = (self.apiUrl + endpoint + "/months?market=KRW-{}&count=3000").format(ticker_code)
                else:
                    url = (self.apiUrl + endpoint + "/months?market=KRW-{}&count=3000&to={}").format(ticker_code, to)

            if url is None:
                return None

            headers = {"accept": "application/json"}
            response = requests.get(url, headers=headers)
            json_data = json.loads(response.text)
            df_temp = pd.DataFrame(json_data)
            if 'candle_date_time_kst' not in df_temp:
                return None
            df = pd.DataFrame()
            #df.columns = ['datetime', 'open', 'close', 'high', 'low', 'volume']
            #df['datetime'] = pd.to_datetime(df_temp['candle_date_time_kst'])
            df['datetime'] = pd.to_datetime(df_temp['candle_date_time_kst'], format='%Y-%m-%dT%H:%M:%S')
            df['open'] = df_temp['opening_price']
            df['close'] = df_temp['trade_price']
            df['high'] = df_temp['high_price']
            df['low'] = df_temp['low_price']
            df['volume'] = df_temp['candle_acc_trade_volume']
            df = df.set_index('datetime')
            df = df.astype(float)
            df["datetime"] = df.index

        if df is None:
            return None

        return df

    def getTickerList(self):
        url = f"{self.apiUrl}/v1/market/all?isDetails=false"
        headers = {"accept": "application/json"}
        response = requests.get(url, headers=headers)

        tickets = response.json()
        return tickets

    def getVirtual_asset_warning(self):
        url = f"{self.apiUrl}/v1/market/virtual_asset_warning"
        headers = {"accept": "application/json"}
        response = requests.get(url, headers=headers)
        warning_list = response.json()
        return warning_list

    # 거래대금이 많은 순으로 코인리스트를 얻는다.
    def getTopCoinList(self, interval, top):
        return

    # 현재 가격 얻어오기
    def getCurrentPrice(self, ticker_code, endpoint='/v1/ticker'):
        headers = {"accept": "application/json"}
        url = (self.apiUrl + endpoint + "?markets=KRW-{}").format(ticker_code)
        response = requests.get(url, headers=headers)

        ticker_state = response.json()
        return ticker_state

    # 잔고 가져오기
    def getBalances(self, ticker_code=None, endpoint='/v1/accounts'):

        payload = {
            'access_key': self.accessKey,
            'nonce': str(uuid.uuid4()),
            'timestamp': round(time.time() * 1000)
        }
        jwt_token = jwt.encode(payload, self.secretKey)
        authorization_token = 'Bearer {}'.format(jwt_token)
        headers = {
            'Authorization': authorization_token
        }
        response = requests.get(self.apiUrl + endpoint, headers=headers)
        balances = response.json()

        """
        [
            {'currency': 'P', 'balance': '78290', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'KRW', 'balance': '4218.401653', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'XRP', 'balance': '13069.27647861', 'locked': '0', 'avg_buy_price': '1917', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'ADA', 'balance': '6941.65484013', 'locked': '0', 'avg_buy_price': '1260', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'BSV', 'balance': '0.00005656', 'locked': '0', 'avg_buy_price': '65450', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'SAND', 'balance': '0.00001158', 'locked': '0', 'avg_buy_price': '544.8', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'AVAX', 'balance': '26.43960509', 'locked': '0', 'avg_buy_price': '60882', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'},
            {'currency': 'XCORE', 'balance': '0.2119', 'locked': '0', 'avg_buy_price': '0', 'avg_buy_price_modified': False, 'unit_currency': 'KRW'}
        ]
        """
        if ticker_code is None:
            return balances
        else:
            for balance in balances:
                if balance['currency'] == ticker_code:
                    return balance

        return None


    def order(self, ticker_code, side, ord_type, volume, price=None, endpoint='/v1/orders'):
        if ord_type=='limit':
            # 지정가 매수 (limit, side=bid) / 매도 (limit, side=ask)
            if price is None:
                return

            requestBody = dict(market='KRW-'+ticker_code, side=side, volume=volume, price=price, ord_type=ord_type)
        else:
            # 시장가 매수 (price, side=bid) / 매도 (market, side=ask)

            if ord_type == 'price':
                requestBody = dict(market='KRW-' + ticker_code, side=side, price=price, ord_type=ord_type)
            else:
                requestBody = dict(market='KRW-' + ticker_code, side=side, volume=volume, ord_type=ord_type)

        # Generate access token
        query = urlencode(requestBody).encode()
        hash = hashlib.sha512()
        hash.update(query)
        query_hash = hash.hexdigest()
        payload = {
            'access_key': self.accessKey,
            'nonce': str(uuid.uuid4()),
            'timestamp': round(time.time() * 1000),
            'query_hash': query_hash,
            'query_hash_alg': 'SHA512',
        }
        jwt_token = jwt.encode(payload, self.secretKey)
        authorization_token = 'Bearer {}'.format(jwt_token)
        headers = {
            'Authorization': authorization_token,
            'Content-Type': 'application/json'
        }

        response = requests.post(self.apiUrl + endpoint, data=json.dumps(requestBody), headers=headers)
        # handle to success or fail
        #print(response.json())
        if response.status_code == 200:
            return True
        return False

    # 시장가 매수한다. 2초 뒤 잔고 데이터 리스트 리턴
    def buyCoinMarket(self, ticker_code, price, count=None):
        if price > 5000:
            if price < 50000:
                self.order(ticker_code, side='bid', ord_type='price', volume=count, price=price)
                buy_price = price
            else:
                repeat = 10
                buy_price = int(price / 1000) * 1000
                buy_amount = int(buy_price / repeat)
                while repeat > 0:
                    self.order(ticker_code, side='bid', ord_type='price', volume=count, price=buy_amount)
                    repeat -= 1
                    time.sleep(0.5)
        else:
            buy_price = 0
        return buy_price

    # 시장가 매도한다. 2초 뒤 잔고 데이터 리스트 리턴
    def sellCoinMarket(self, ticker_code, price, count):
        return self.order(ticker_code, side='ask', ord_type='market', volume=count, price=price)

    # 지정가 매수한다. 2초 뒤 잔고 데이터 리스트 리턴
    def buyCoinLimit(self, ticker_code, price, count):
        return self.order(ticker_code, side='bid', ord_type='limit', volume=count, price=price)

    # 지정가 매도한다. 2초 뒤 잔고 데이터 리스트 리턴
    def sellCoinLimit(self, ticker_code, price, count):
        return self.order(ticker_code, side='ask', ord_type='limit', volume=count, price=price)

    def getOrderBook(self, ticker_code, endpoint='/v1/orderbook'):
        """
        필드             설명	            타입
        market	        마켓 코드	        String
        timestamp	    호가 생성 시각	    Long
        total_ask_size	호가 매도 총 잔량	Double
        total_bid_size	호가 매수 총 잔량	Double
        orderbook_units	호가	            List of Objects
            > ask_price	    매도호가	        Double
            > bid_price	    매수호가	        Double
            > ask_size	    매도 잔량	        Double
            > bid_size	    매수 잔량	        Double
        """
        headers = {"accept": "application/json"}
        url = (self.apiUrl + endpoint + "?markets=KRW-{}").format(ticker_code)
        response = requests.get(url, headers=headers)

        # 매도 총 잔량: sum([units['ask_size'] for units in orders[0]['orderbook_units']])
        # 매수 총 잔량: sum([units['bid_size'] for units in orders[0]['orderbook_units']])
        orders = response.json()
        return orders