import argparse
import eth_account
import utils
import json
import time
import os
import csv
import pandas as pd
import sys
import requests

from ta.trend import SMAIndicator
from eth_account.signers.local import LocalAccount
from hyperliquid.exchange import Exchange
from hyperliquid.info import Info
from hyperliquid.utils import constants
from websocket_manager import WebsocketManager
import matplotlib.pyplot as plt
from datetime import datetime


start_time = time.time()
coin_mid_price = 0
cancel_minute = 300
sma_span = 14
histories = {}

def trading_strategy(coin_mid_price, coin, sz):

    current_time = time.strftime("%Y-%m-%d %H:%M:%S")

    # levarage = 1
    # pre_minutes = 1
    # percent = 3


    csv_file_name = f'file\price_data_{coin}.csv'
    df = pd.read_csv(csv_file_name)
    df['Timestamp'] = pd.to_datetime(df['Timestamp'])
    df.set_index('Timestamp', inplace=True)
    sma_indicator = SMAIndicator(df['Price'], window=sma_span)
    df['SMA'] = sma_indicator.sma_indicator()
    plot_chart(df, coin)
    latest_sma = df['SMA'].iloc[-1]
    latest_sma_str = '{:.1f}'.format(latest_sma)

    print(f"{current_time} The mid price of {coin} is: {coin_mid_price} SMA: {latest_sma_str}")




    positions = check_positions()
    if positions is None or len(positions) == 0:

        orders = check_orders()
        if orders is None or len(orders) == 0:
            print("There is no order.")


            if coin_mid_price > latest_sma:
                limit_px = int(latest_sma)
                is_buy = True
                try:
                    print("★Buying")  # Debugging print
                    print(str(limit_px))
                    # place_order(coin, is_buy, sz,limit_px)
                    place_market_order(coin, is_buy, sz)



                except Exception as e:
                    print(f"Error placing buy order: {e}")  # Error handling
            elif coin_mid_price < latest_sma:
                limit_px = int(latest_sma)
                is_buy = False
                try:
                    print("★Selling")  # Debugging print
                    print(str(limit_px))
                    # place_order(coin, is_buy, sz,limit_px)
                    place_market_order(coin, is_buy, sz )


                    # place_order(coin, True, sz,limit_px)

                except Exception as e:
                    print(f"Error placing sell order: {e}")  # Error handling


            # positions = check_positions()
            # for position in positions:
            #     # print(json.dumps(position, indent=2))
            #     coin = position["coin"]
            #     quantity = float(position["szi"])

        else:
            print("There is an order.")
            # print(orders)

    else:
        t = time.time() - start_time
        print(t)

        for position in positions:
            # print(f"Position data: {position}")
            # print(position['unrealizedPnl'])
            # print(json.dumps(position, indent=2))
            unrealized_pnl = float(position['unrealizedPnl'])
            entry_px = float(position['entryPx'])
            position_sz = float(position['szi'])
            if(position_sz < 0):
                is_buy = True
            else:
                is_buy = False

            print(str(unrealized_pnl))
            # print(sz)

            if(unrealized_pnl > 0.05):
                print("Profit")

                place_market_order(coin, is_buy, sz,"win")


            else:
                print("Loss")
                if time.time() - start_time > cancel_minute:
                    print("time Cancel position")

                    place_market_order(coin, is_buy, sz)

                if unrealized_pnl < -0.5:
                    print("pnl loss Cancel position")

                    place_market_order(coin, is_buy, sz)


def place_order(coin, is_buy, sz, limit_px):
    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    exchange = Exchange(account, constants.MAINNET_API_URL)

    print(f"We try to Market {'Buy' if is_buy else 'Sell'} {sz} {coin}.")

    # "Alo": "At the Limit Order" の略で、指値注文を意味します。指値注文は、取引者が特定の価格で資産を売買することを指示する注文です。たとえば、資産の価格が指定した価格に達したときにのみ取引が発生するように指定されます。
    # "Ioc": "Immediate or Cancel Order" の略で、即時成行きまたはキャンセル注文を意味します。この注文タイプは、注文を即時に部分的または完全に執行できる場合にのみ、注文が有効であり、そうでない場合は即座にキャンセルされることを意味します。一部の取引所では、この注文タイプは「FOK（Fill or Kill）」とも呼ばれます。
    # "Gtc": "Good 'til Canceled Order" の略で、有効期限切れまで有効な注文を意味します。つまり、注文が完全に執行されるか、取引者が注文をキャンセルするまで、注文は有効です。
    order_type = {
        "limit": {
            "tif": "Alo"
            # "tif": "Alo" | "Ioc" | "Gtc"
        }

        # "trigger": {
        #     "triggerPx": 1600 if is_buy else 2400,
        #     "isMarket": False,
        #     "tpsl": "tp"
        # }
    }
    order_result = exchange.order(coin, is_buy, sz,limit_px,order_type)
    print(order_result)


def place_market_order(coin, is_buy, sz, reason = ""):
    global coin_mid_price
    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    exchange = Exchange(account, constants.MAINNET_API_URL)

    print(f"We try to Market {'Buy' if is_buy else 'Sell'} {sz} {coin}.")


    order_result = exchange.market_open(coin, is_buy, sz)



    if order_result["status"] == "ok":
        global start_time
        start_time = time.time()


        info = Info(constants.MAINNET_API_URL, skip_ws=True)
        user_state = info.user_state(account.address)
        balance = float(user_state['marginSummary']['accountValue'])
        current_time = time.strftime("%Y-%m-%d %H:%M:%S")

        history = []
        history['current_time'] = current_time
        history['balance'] = balance
        if reason != "":
            history['reason'] = reason
        # history['order_result'] = order_result

        # global histories
        # histories.append(history)
        # print(histories)
        # last_key = next(reversed(histories), None)

        # if
        # if histories[last_key] is not None:
        # print(f"start_time:{current_time}, balance:{balance}")


        # latest_sma_str = '{:.1f}'.format(latest_sma)


        # notify_discord(f"start_time:{current_time}, balance:{'{:.2f}'.format(balance)}")
        # sys.exit()

        update_csv_with_sma(current_time, coin_mid_price, coin,history)


        for status in order_result["response"]["data"]["statuses"]:
            try:
                filled = status["filled"]
                print(f'Order #{filled["oid"]} filled {filled["totalSz"]} @{filled["avgPx"]}')
            except KeyError:
                print(f'Error: {status["error"]}')


def notify_discord(message):
    discord_webhook_url = 'https://discord.com/api/webhooks/1221397301331755089/GlLu5cm3fVPFfbud0xNlpoaXCzLEDM0UMFlxYJ_0kEZwo97W2cL7Wfq3Sd3pdmf6AEk_'
    data = {"content": message}
    requests.post(discord_webhook_url, data=data)


def cancel_position(coin, cloid):
    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    exchange = Exchange(account, constants.MAINNET_API_URL)


    # order_result = exchange.order(coin, is_buy, sz,limit_px,order_type)
    # print(order_result)
    cancel_order = exchange.cancel_by_cloid(coin,cloid)

    return cancel_order


def update_csv_with_sma(timestamp, price, coin ,history = None):

    current_script_directory = os.path.dirname(os.path.abspath(__file__))
    csv_file_name = f'file/price_data_{coin}.csv'
    csv_file_path = os.path.join(current_script_directory, csv_file_name)
    if not os.path.exists(csv_file_path):
        print(f"Creating new csv file: {csv_file_path}")  # Use csv_file_path instead of csv_file_name
        with open(csv_file_path, 'w', newline='') as file:  # Use csv_file_path instead of csv_file_name
            writer = csv.writer(file)
            writer.writerow(["Timestamp", "Price"])

    df = pd.read_csv(csv_file_path)  # Use csv_file_path instead of csv_file_name
    df['Timestamp'] = pd.to_datetime(df['Timestamp'])
    df.set_index('Timestamp', inplace=True)
    df.loc[timestamp] = price
    sma_indicator = SMAIndicator(df['Price'], window=sma_span)
    df['SMA'] = sma_indicator.sma_indicator()
    df.to_csv(csv_file_path)  # Use csv_file_path instead of csv_file_name



def plot_chart(df, coin):


    # 全てのフィギュアを閉じる
    plt.close('all')


    plt.switch_backend('Agg')
    plt.figure(figsize=(10,6))
    plt.plot(df.index, df['Price'], label='Price')
    plt.plot(df.index, df['SMA'], label=f'SMA {str(sma_span)}', alpha=0.7)
    plt.title(f'Price Chart with SMA for {coin}')
    plt.xlabel('Date')
    plt.ylabel('Price')
    plt.legend()
    plt.grid(True)
    plt.savefig(f'file\{coin}_price_chart.png')



def handle_allmids_update(allmids, coin, sz):


    mids = allmids["data"]["mids"]

    coin_mid_price_str = mids.get(coin)
    # print(coin_mid_price_str)
    if coin_mid_price_str is not None:
        global coin_mid_price
        coin_mid_price = float(coin_mid_price_str)
        current_time = time.strftime("%Y-%m-%d %H:%M:%S")
        update_csv_with_sma(current_time, coin_mid_price, coin)
        trading_strategy(coin_mid_price, coin, sz)
    else:
        print(f"{coin} not found in the AllMids data.")

    # print(mids)
    # sys.exit()
    time.sleep(5)


def check_positions():
    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    info = Info(constants.MAINNET_API_URL, skip_ws=True)
    user_state = info.user_state(account.address)

    # print(user_state)

    positions = []
    for position in user_state["assetPositions"]:
        if float(position["position"]["szi"]) != 0:
            positions.append(position["position"])

    # if len(positions) > 0:
    #     # print("position data:")
    #     for position in positions:
    #         # print(json.dumps(position, indent=2))
    #         coin = position["coin"]
    #         quantity = float(position["szi"])
    # else:
        # print("No open positions")

    return positions


def check_orders():
    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    info = Info(constants.MAINNET_API_URL, skip_ws=True)
    orders = info.open_orders(account.address)



    return orders


if __name__ == "__main__":


    subscription_id = None

    coin = 'BTC'
    is_buy = False
    # limit_px = 100000
    #BTCのサイズ 0.001だったら0.001BTC
    size = 0.001

    # positions = check_positions()
    # print(positions)
    # sys.exit()


    config = utils.get_config()
    account: LocalAccount = eth_account.Account.from_key(config["secret_key"])
    info = Info(constants.MAINNET_API_URL, skip_ws=True)
    user_state = info.user_state(account.address)
    balance = float(user_state['marginSummary']['accountValue'])
    current_time = time.strftime("%Y-%m-%d %H:%M:%S")
    notify_discord(f"start_time:{current_time}, balance:{'{:.2f}'.format(balance)}")
    # sys.exit()



    # ts = int(user_state["time"])
    # # time = datetime.fromtimestamp(user_state["time"])
    # # ts = 1653615922

    # print("-- タイムスタンプ --")
    # print(ts)
    # print(type(ts))



    # # タイムスタンプ  → datetime
    # dt = datetime.fromtimestamp(ts)
    # # print(time)
    # print(dt.strftime("%Y-%m-%d %H:%M:%S"))
    # print(json.dumps(user_state, indent=2))
    # sys.exit()
    # print(start_time)
    # print(positioned_time.strftime("%Y-%m-%d %H:%M:%S"))


    # time.sleep(5)

    # t = time.time() - start_time

    # print(t)
    # sys.exit()



    base_url = "wss://api.hyperliquid.xyz/ws"
    websocket_manager = WebsocketManager(base_url)
    websocket_manager.start()
    allmids_subscription = {"type": "allMids"}



    try:
        # asyncio.run(main())
        subscription_id = websocket_manager.subscribe(allmids_subscription, lambda allmids: handle_allmids_update(allmids, coin, size))

        # ここで無限ループを回して、Ctrl + C が押されるまで待機する
        while True:
            pass
    except KeyboardInterrupt:
        print("KeyboardInterrupt: Stopping WebSocket connection.")
        print(subscription_id)
        # os._exit()
        websocket_manager.unsubscribe(allmids_subscription,subscription_id)


