import uvicorn
import fastapi
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from linebot import LineBotApi, WebhookHandler
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage, FollowEvent, TemplateSendMessage, ButtonsTemplate, URITemplateAction,
)
import dataset
import requests
import json
import qrcode
# from PIL import Image
# import base64, io
from random import randrange
import models
import datetime as dt

app = fastapi.FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# bot config
line_bot_api = LineBotApi("SJT7VPT4RMQFLcS27jQBy3FcC24gtDrkcwJWZ5Xzqesr5T78LOKudHEJzt0k3b2S7n4KPwf27J7DVz2c8NQ4plSaaQylEeB1cYrfejaE/RPG/lCIQBYe4iBTzo26s4i2PcmT89837per/lTyvhVIKAdB04t89/1O/w1cDnyilFU=")
handler = WebhookHandler("411ae3ef7e766739ed2c2c27b249d010")

# callback event
@app.post("/callback")
async def callback(request: fastapi.Request):
    signature = request.headers['X-Line-Signature']
    body = await request.body()
    handler.handle(body.decode('utf-8'), signature)
    return 'OK'

# follow event
@handler.add(FollowEvent)
def handle_follow(event):
    # get user id when follow
    real_user_id = event.source.user_id

    # db connect and search
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table = db['users']
    result1 = table.find_one(userid=real_user_id)

    # 都存在db的話
    if result1:
        db.close()
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='很高興再見到您！'))

    # 建立全新使用者
    else:
        # create user account api
        url = 'https://nft-api-staging.joyso.io/api/v1/accounts'
        headers = {'Authorization': 'Basic bmZ0OmMxOTEzOWMzYjM3YjdjZWU3ZmY3OTFiZGU3NzdjZWNl'}

        # setup for temp use (unique id)
        rand_num = str(randrange(99999))
        user_id = event.source.user_id + rand_num
        data = 'uid=' + user_id
        r = requests.post(url=url, headers=headers, data=data)

        # extract the account address
        dict_str = json.loads(r.text)
        user_account = dict_str['account']
        user_address = user_account['address']

        # generate qr code from user id
        qr = qrcode.QRCode(
            version=1,
            box_size=10,
            border=5)
        qr.add_data(user_address)
        qr.make(fit=True)
        img_qr = qr.make_image(fill='black', back_color='white')
        filename = "/var/www/ArkCard-Linebot/ArkCard-web/qrcode/" + real_user_id + '.png'
        img_save = img_qr.save(filename)

        # add to db
        data = dict(userid=real_user_id, useraddress=user_address)
        table.insert(data)

        db.close()

        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='歡迎加入好友'))

# message handler
@handler.add(MessageEvent, message=TextMessage)
def message(event):
    if '我要發送' in event.message.text:
        button_template_message = ButtonsTemplate(
            title=' ',
            text='點擊並打開收藏的NFT，可以選擇想要發送的NFT給對方！',
            actions=[
                URITemplateAction(
                    label='打開發送頁',
                    uri='https://ark.cards/collect.html?' + event.source.user_id),])
        line_bot_api.reply_message(
            event.reply_token,
            TemplateSendMessage(
                alt_text="Receive",
                template=button_template_message))

    elif '我要接收' in  event.message.text:
        button_template_message = ButtonsTemplate(
            title=' ',
            text='點擊並打開接收頁面，即可分享接收地址給對方！',
            actions=[
                URITemplateAction(
                    label='打開接收頁',
                    uri='https://ark.cards/qr-code.html?' + event.source.user_id),])
        line_bot_api.reply_message(
            event.reply_token,
            TemplateSendMessage(
                alt_text="Receive",
                template=button_template_message))

    elif 'NFT商店' in event.message.text:
        button_template_message = ButtonsTemplate(
            title=' ',
            text='點擊並打開NFT商品頁，就可以購買您所想要的NFT商品哦！',
            actions=[
                URITemplateAction(
                    label='打開NFT商品頁',
                    uri='https://ark.cards/shop.html?' + event.source.user_id),])
        line_bot_api.reply_message(
            event.reply_token,
            TemplateSendMessage(
                alt_text="Receive",
                template=button_template_message))

    elif 'NFT收藏' in event.message.text:
        button_template_message = ButtonsTemplate(
            title=' ',
            text='點擊並打開收藏的NFT，可以查看收到的NFT！',
            actions=[
                URITemplateAction(
                    label='打開收藏頁',
                    uri='https://ark.cards/collect.html?' + event.source.user_id), ])
        line_bot_api.reply_message(
            event.reply_token,
            TemplateSendMessage(
                alt_text="Receive",
                template=button_template_message))
    else:
        button_template_message = ButtonsTemplate(
            title=' ',
            text='更多的服務內容，歡迎請上我們的官網！',
            actions=[
                URITemplateAction(
                    label='ArkCard的官網',
                    uri='https://ark.cards'),])
        line_bot_api.reply_message(
            event.reply_token,
            TemplateSendMessage(
                alt_text="Receive",
                template=button_template_message))

# nft collection api
@app.get("/collection/{userid}")
def collection(userid):
    # 連到ownership表單去找，並回傳json
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table1 = db['ownership']
    nft = {}
    i = 0
    if not table1.find_one(userid=userid):
        db.close()
        return "error: user don't have any nft"
    else:
        results = db.query("SELECT nftid, amount FROM arkcard.ownership WHERE userid IN (SELECT userid FROM arkcard.ownership WHERE userid = '"+userid+"');")
        for item in results:
            nft[i] = item
            i += 1
        db.close()
        return nft

# receive handler
@app.get("/receive/{userid}")
def receive(userid):
    # 確定要傳送的對象，到user去找到它
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table = db['users']

    if not table.find_one(userid=userid):
        db.close()
        return "ERROR: User Not Found"
    else:
        result = table.find_one(userid=userid)
        db.close()
        return {"userid": result['userid'], "address": result['address']}

# send handler
@app.post("/send")
async def receive(userModel : models.TransactionNft):
    # 從網頁上選定好商品，在選定好數量後輸入收方地址;將把該商品轉到收方，並存下交易記錄
    # 從ownership找到該userid的擁有人，再把擁有人userid改為收方，並在trans留下記錄
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table1 = db['users']
    table2 = db['ownership']
    table3 = db['transactions']

    # find the userid from the selected nft
    fromuser = userModel.fromuser
    address = userModel.address
    result1 = table1.find_one(address=address)
    result2 = table1.find_one(userid=fromuser)
    # 例:
    # {
    #   "fromuser": "a01",
    #   "address": "0x4cd0ea8b1bdb5ab9249d96ccf3d8a0d3ada2bc76",
    #   "dic": {"10":"2", "20":"3"}
    # }
    # 第一個為nftid, 第二個為amount，可以同時輸入多組nft給同一人
    the_list = userModel.dic
    fr_token = "from token"
    _hash = "hash"

    # confirm if the user exist
    # 如果沒有這個接受者，回傳錯誤
    if not result1:
        db.close()
        return {'msg': 'user not found'}
    # 確認發送方的nft夠不夠發放
    else:
        for nftid, amount in the_list.items():
            print("nft: "+nftid+"，數量:"+ amount)
            # 直接針對取到的值去做確認數量和更新表單，最後再記錄
            # 確認sender的總數
            result3 = db.query('SELECT SUM(amount) AS total FROM arkcard.ownership WHERE userid="' + fromuser + '" AND nftid ="' + nftid + '";')

            # 把總數撈出來，存到total
            for row in result3:
                rows = row
            total = rows['total']

            # 如果總數不夠，就報錯回傳
            if total == None or total < int(amount):
                db.close()
                return {"messge: NFT not enough"}

            # 總數夠，在ownership刪去數量，並再新增ownership一筆
            else:
                # 今天到這，需要找出怎麼扣除總數的方式，目前找到的總數，會更新到資料欄位，導致原本才100個的，會被更新成499個，因為全加到一個
                # 找接收者的userid
                to_user = result1['userid']
                old_address = result2['address']
                print(total)
                new_amount = total - int(amount)
                # 更新原user總數
                result4 = db.query('DELETE FROM arkcard.ownership WHERE userid="' + fromuser + '" AND nftid ="' + nftid + '";')
                result5 = db.query('UPDATE arkcard.ownership SET amount = amount - "' + amount + '" WHERE userid = "' + fromuser + '" AND nftid = "' + nftid + '" LIMIT 1;')
                new_data1 = dict(userid=fromuser, address=old_address, nftid=nftid, amount=new_amount)
                table2.insert(new_data1)
                # 增加新user數量
                new_data2 = dict(userid=to_user, address=address, nftid=nftid, amount=int(amount))
                table2.insert(new_data2)

                # 在transactions記錄from和to, 以及時間等等
                now = dt.datetime.now()
                trans_data = dict(to_token=address, time=now, amount=int(amount), userid=fromuser, from_token=fr_token, hash=_hash)
                table3.insert(trans_data)
        db.close()
        return {"messge: NFT 夠"}

# shop handler
@app.get("/shop")
def shop():
    # 為了顯示正確的nft網頁和數量，直接顯示nft表出來
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    result1 = db.query('SELECT nftid, launched, amount, type, title, context FROM arkcard.nft WHERE launched="y" AND nftid NOT IN(SELECT nftid FROM arkcard.nft_to_event);')
    nfts = {}
    i = 0
    for row in result1:
        nfts[i] = row
        i += 1
    return nfts
    db.close()

@app.post("/buy")
async def buy(userModel : models.BuyNft):
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table1 = db['users']
    table2 = db['ownership']
    table3 = db['nft']
    table4 = db['transactions']
    shops = "shop"
    hash = "hash"

    # input
    the_list = userModel.dic
    address = userModel.address
    # 例:
    # {
    #   "address": "d04",
    #   "dic": {"1":"2","2":"3"}
    # }

    result1 = table1.find_one(address=address)

    # 找不到該使用者就回報錯誤
    if not result1:
        db.close()
        return "該用戶不存在！如果有疑問，請洽網站的服務信箱！"
    else:
        for nftid, amount in the_list.items():
            # 先user找到該使用者的userid留著備用
            userid = result1['userid']

            # 更新到ownership
            owner_data = [dict(userid=userid, address=address, nftid=nftid, amount=int(amount))]
            table2.insert_many(owner_data)

            # 更新nft數量
            result3 = table3.find_one(nftid=nftid)
            nft_total = result3['amount']
            total = int(nft_total) - int(amount)
            nft_data = dict(nftid=nftid, amount=int(total))
            table3.update(nft_data, ['nftid'])

            # 最後再來處理transactions
            now = dt.datetime.now()
            trans_data = dict(to_token=address, time=now, amount=int(amount), userid=userid, from_token=shops, hash=hash)
            table4.insert(trans_data)
        db.close()
        return "您已購買成功！"

@app.post("/event")
async def nftdrops(userModel : models.NftDrops):
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    table1 = db['nft_drop_list']

    # input
    userid = userModel.userid
    email = userModel.email

    the_current_event = eventPeriod()

    if the_current_event == 0:
        return "目前沒有任何進行中的活動"
    else:
        # 再針對有上架的 event 填寫客戶到 nftdroplist
        now = dt.datetime.now()
        result1 = table1.find_one(userid=userid)
        if not result1:
            for key, item in the_current_event.items():
                table1.insert(dict(eventid=item, userid=userid, email=email, time=now))
            db.close()
            return "完成加入"
        else:
            db.close()
            return "該資料已存在"

@app.post("/eventPeriod")
def eventPeriod():
    # db connect
    db = dataset.connect('mysql://choozmo:pAssw0rd@db.ptt.cx:3306/arkcard?charset=utf8mb4')
    # 設定event始終期間，與上架與否
    table3 = db['event']
    now = dt.datetime.now()
    # 先去找 event 表，確認在期間之內
    result = db.query("SELECT * FROM arkcard.event WHERE start < NOW() and NOW() < end and launched='yes';")
    rows = {}
    i = 0
    for row in result:
        rows[i] = row['eventid']
        i += 1
    return rows
    db.close()




if __name__ == '__main__':
    uvicorn.run("main:app", host="0.0.0.0", port=8228, reload=True,ssl_context=('/etc/letsencrypt/live/ark.cards/fullchain.pem', '/etc/letsencrypt/live/ark.cards/privkey.pem'))