import secrets
import time
from typing import List, Optional, Union
from uuid import uuid4

from . import db
from .helpers import generate_bleskomat_lnurl_hash
from .models import Bleskomat, BleskomatLnurl, CreateBleskomat


async def create_bleskomat(data: CreateBleskomat, wallet_id: str) -> Bleskomat:
    bleskomat_id = uuid4().hex
    api_key_id = secrets.token_hex(8)
    api_key_secret = secrets.token_hex(32)
    api_key_encoding = "hex"
    await db.execute(
        """
        INSERT INTO bleskomat.bleskomats (id, wallet, api_key_id, api_key_secret, api_key_encoding, name, fiat_currency, exchange_rate_provider, fee)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
        (
            bleskomat_id,
            wallet_id,
            api_key_id,
            api_key_secret,
            api_key_encoding,
            data.name,
            data.fiat_currency,
            data.exchange_rate_provider,
            data.fee,
        ),
    )
    bleskomat = await get_bleskomat(bleskomat_id)
    assert bleskomat, "Newly created bleskomat couldn't be retrieved"
    return bleskomat


async def get_bleskomat(bleskomat_id: str) -> Optional[Bleskomat]:
    row = await db.fetchone(
        "SELECT * FROM bleskomat.bleskomats WHERE id = ?", (bleskomat_id,)
    )
    return Bleskomat(**row) if row else None


async def get_bleskomat_by_api_key_id(api_key_id: str) -> Optional[Bleskomat]:
    row = await db.fetchone(
        "SELECT * FROM bleskomat.bleskomats WHERE api_key_id = ?", (api_key_id,)
    )
    return Bleskomat(**row) if row else None


async def get_bleskomats(wallet_ids: Union[str, List[str]]) -> List[Bleskomat]:
    if isinstance(wallet_ids, str):
        wallet_ids = [wallet_ids]
    q = ",".join(["?"] * len(wallet_ids))
    rows = await db.fetchall(
        f"SELECT * FROM bleskomat.bleskomats WHERE wallet IN ({q})", (*wallet_ids,)
    )
    return [Bleskomat(**row) for row in rows]


async def update_bleskomat(bleskomat_id: str, **kwargs) -> Optional[Bleskomat]:
    q = ", ".join([f"{field[0]} = ?" for field in kwargs.items()])
    await db.execute(
        f"UPDATE bleskomat.bleskomats SET {q} WHERE id = ?",
        (*kwargs.values(), bleskomat_id),
    )
    row = await db.fetchone(
        "SELECT * FROM bleskomat.bleskomats WHERE id = ?", (bleskomat_id,)
    )
    return Bleskomat(**row) if row else None


async def delete_bleskomat(bleskomat_id: str) -> None:
    await db.execute("DELETE FROM bleskomat.bleskomats WHERE id = ?", (bleskomat_id,))


async def create_bleskomat_lnurl(
    *, bleskomat: Bleskomat, secret: str, tag: str, params: str, uses: int = 1
) -> BleskomatLnurl:
    bleskomat_lnurl_id = uuid4().hex
    hash = generate_bleskomat_lnurl_hash(secret)
    now = int(time.time())
    await db.execute(
        """
        INSERT INTO bleskomat.bleskomat_lnurls (id, bleskomat, wallet, hash, tag, params, api_key_id, initial_uses, remaining_uses, created_time, updated_time)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """,
        (
            bleskomat_lnurl_id,
            bleskomat.id,
            bleskomat.wallet,
            hash,
            tag,
            params,
            bleskomat.api_key_id,
            uses,
            uses,
            now,
            now,
        ),
    )
    bleskomat_lnurl = await get_bleskomat_lnurl(secret)
    assert bleskomat_lnurl, "Newly created bleskomat LNURL couldn't be retrieved"
    return bleskomat_lnurl


async def get_bleskomat_lnurl(secret: str) -> Optional[BleskomatLnurl]:
    hash = generate_bleskomat_lnurl_hash(secret)
    row = await db.fetchone(
        "SELECT * FROM bleskomat.bleskomat_lnurls WHERE hash = ?", (hash,)
    )
    return BleskomatLnurl(**row) if row else None
