import base64
import hashlib
import hmac
import json
import time
from urllib.parse import urlparse, parse_qsl
from requests import Request
import requests
import re

from src.vaultody.exceptions import VaultodyException


class VaultodyService:

    def __init__(self, vault_id: str, api_key: str, 
                 api_secret: str, api_passphrase: str) -> None:
        self.base_url = "https://rest.vaultody.com"
        self.vault_id = vault_id
        self.api_key = api_key
        self.api_secret = api_secret
        self.api_passphrase = api_passphrase

    
    def sign_request(self, request: Request):
        prepared_request = request.prepare()
        # Get current timestamp
        timestamp = str(int(time.time()))

        request_method = request.method.upper()
        request_url = prepared_request.url
        request_body = prepared_request.body

        # Convert request body to string if it's bytes
        if isinstance(request_body, bytes):
            request_body = request_body.decode('utf-8')
        
        # If request body is None, use empty JSON
        if request_method == 'GET' or request_body is None:
            request_body = '{}'
        
        # Remove newlines preserving quoted content
        regex = r'\n(?=(?:[^"]*"[^"]*")*[^"]*$)'
        request_body = re.sub(regex, '', request_body)
        
        # Remove extra whitespaces outside of quotes
        request_body = re.sub(r'(".*?")|\s+', r'\1', request_body)

        # Parse URL components
        parsed_url = urlparse(request_url)
        path = parsed_url.path
        
        # Handle query parameters
        query_params = dict(parse_qsl(parsed_url.query)) if parsed_url.query else {}
        
        # Prepare message to sign
        message_to_sign = (
            timestamp + 
            request_method.upper() + 
            path + 
            request_body + 
            json.dumps(query_params, separators=(',', ':'))
        )
        
        # Decode base64 secret
        secret_bytes = base64.b64decode(self.api_secret)
        
        # Create HMAC-SHA256 signature
        signature = base64.b64encode(
            hmac.new(secret_bytes, message_to_sign.encode('utf-8'), hashlib.sha256).digest()
        ).decode('utf-8')

        # Prepare authentication headers
        prepared_request.headers['x-api-timestamp'] = timestamp
        prepared_request.headers['x-api-sign'] = signature
        prepared_request.headers['x-api-key'] = self.api_key
        prepared_request.headers['x-api-passphrase'] = self.api_passphrase
        
        return prepared_request

    def list_vaults(self):
        url = f"{self.base_url}/vaults/test"
        response = self.sign_request(request=requests.Request('GET', url))
        s = requests.Session()

        response = s.send(response, timeout=60)
        
        if response.ok:
            return response.json()

    def generate_address(self, blockchain, network, label: str):
        """
        Generates a new address for a specified blockchain and network with a given label.

        Parameters:
        - blockchain (str): The name of the blockchain (e.g., 'bitcoin', 'ethereum').
        - network (str): The specific network within the blockchain (e.g., 'mainnet', 'testnet').
        - label (str): A label to associate with the generated address for identification.

        Returns:
        - str: The generated address.
        """
        url = f"{self.base_url}/vaults/{self.vault_id}/{blockchain}/{network}/addresses"
        address: str = ""

        request_body = {
            "data": {
                "item": {
                    "label": label
                }
            }
        }

        req = self.sign_request(Request('POST', url, json=request_body))

        s = requests.Session()

        response = s.send(req, timeout=60)

        print(response.json(), req.body)
        
        if response.ok:
            address = response.json()['data']['item']['address']
            return address
        return None
    

    def send_from_address(
            self, blockchain, network, amount, 
            address, 
            fee_priority="slow", context="", 
            return_response=False):
        
        url = f"{self.base_url}/vaults/{self.vault_id}/{blockchain}/{network}/transaction-requests"
        request_body = {
            "context": context,
            "data": {
                "item": {
                    "feePriority": fee_priority,
                    "recipients": [
                        {
                            "address": address,
                            "amount": str(amount)
                        }
                    ]
                }
            }
        }

        response = self.sign_request(Request('POST', url, json=request_body))
        s = requests.Session()

        response = s.send(response, timeout=60)
        
        if return_response:
            return response
        
        if response.ok:
            return response.json()

    

    def send_from_address_token(
            self, blockchain, network, amount, address, 
            sender_address):
        url = f"{self.base_url}/vaults/{self.vault_id}/{blockchain}/{network}/addresses/{sender_address}/feeless-transaction-requests"
        request_body = {
            "context": "",
            "data": {
                "item": {
                    "recipientAddress": address,
                    "amount": str(amount),
                }
            }
        }

        response = self.sign_request(Request('POST', url, json=json.dumps(request_body)))
        
        s = requests.Session()

        response = s.send(response, timeout=60)
        
        if response.ok:
            return response.json()
    

    def flush_btc(self, amount, address):
        return self.send_from_address("bitcoin", "mainnet", amount, address, return_response=True)
    

    def list_addresses(self, blockchain, limit):

        url = f"{self.base_url}/vaults/{self.vault_id}/{blockchain}/addresses"
        response = self.sign_request(request=requests.Request('GET', url, params={'limit': limit}))
        s = requests.Session()

        response = s.send(response, timeout=60)
        
        if response.ok:
            return response.json()
        else:
            print(response.text)
            print(response.json())
    
    

    def list_assets(self):
        url = f"{self.base_url}/assets/MAIN"
        response = self.sign_request(request=requests.Request('GET', url))
        s = requests.Session()

        response = s.send(response, timeout=60)
        
        if response.ok:
            return response.json()
        else:
            raise VaultodyException(response.text)
        



