
from datetime import date, datetime
from uuid import uuid4
from google.protobuf.any_pb2 import Any
from google.protobuf.timestamp_pb2 import Timestamp

from fintekkers.models.position.field_pb2 import FieldProto
from fintekkers.models.position.position_filter_pb2 import PositionFilterProto
from fintekkers.models.position.position_util_pb2 import FieldMapEntry
from fintekkers.models.security.coupon_frequency_pb2 import NO_COUPON, SEMIANNUALLY
from fintekkers.models.security.coupon_type_pb2 import FIXED, FLOAT, ZERO
from fintekkers.models.security.identifier.identifier_pb2 import IdentifierProto
from fintekkers.models.security.identifier.identifier_type_pb2 import CUSIP
from fintekkers.models.security.security_pb2 import SecurityProto
from fintekkers.models.security.security_quantity_type_pb2 import ORIGINAL_FACE_VALUE
from fintekkers.models.security.security_type_pb2 import BOND_SECURITY, FRN, SecurityTypeProto
from fintekkers.models.util.local_timestamp_pb2 import LocalTimestampProto
from fintekkers.models.util.uuid_pb2 import UUIDProto

from fintekkers.requests.security.create_security_request_pb2 import CreateSecurityRequestProto
from fintekkers.requests.security.query_security_request_pb2 import QuerySecurityRequestProto

from fintekkers.wrappers.models.util.date_utils import get_date_proto
from fintekkers.wrappers.models.util.serialization import ProtoSerializationUtil

class CreateSecurityRequest():
    @staticmethod
    def create_ust_security_request(
        cusip:str,
        cash_security:SecurityProto,
        security_type:SecurityTypeProto=SecurityTypeProto.BOND_SECURITY,
        coupon_rate:float=0.0,
        spread:float=0.0,
        face_value:float=0.0,
        issue_date:date=date.today(),
        dated_date:date=date.today(),
        maturity_date:date=date.today(),
    ):
        '''
            Creates a request to create a security representing a US treasury (bills, notes and bonds)

                Parameters:
                        Parameters are already in protos, see type hints

                Returns:
                        request (CreateSecurityRequest): A request wrapper, with the fields attached
        '''
        id = IdentifierProto(identifier_type=CUSIP, identifier_value=cusip)

        security_type = BOND_SECURITY
        coupon_frequency = SEMIANNUALLY
        coupon_type = FIXED

        # if security_type == TIPS: 
        #     security_type = TIPS
        if security_type == FRN:
            # security_type = FRN
            coupon_type = FLOAT
            coupon_rate = spread
        if security_type == ZERO:
            coupon_type = ZERO
            coupon_frequency = NO_COUPON

        issue_date_proto = get_date_proto(issue_date)
        dated_date_proto = get_date_proto(dated_date) if dated_date != None else None
        maturity_date_proto = get_date_proto(maturity_date)

        security_proto:SecurityProto = SecurityProto(
            as_of=LocalTimestampProto(time_zone="America/New_York", timestamp=Timestamp(seconds=1,nanos=0)),
            uuid=UUIDProto(raw_uuid=uuid4().bytes),
            issuer_name="US Government",
            identifier=id,
            issue_date=issue_date_proto,
            dated_date=dated_date_proto,
            maturity_date=maturity_date_proto,
            security_type=security_type,
            quantity_type=ORIGINAL_FACE_VALUE, 
            settlement_currency=cash_security,
            coupon_frequency=coupon_frequency,
            coupon_type=coupon_type,
            coupon_rate=ProtoSerializationUtil.serialize(coupon_rate),
            asset_class="Fixed Income",
            face_value=ProtoSerializationUtil.serialize(face_value),
        )

        proto:CreateSecurityRequestProto = CreateSecurityRequestProto(
            security_input=security_proto
        )

        return CreateSecurityRequest(proto=proto)
    
    def __init__(self, proto:CreateSecurityRequestProto):
        self.proto = proto

class QuerySecurityRequest():
    @staticmethod
    def create_query_request(fields:dict):
        '''
        Returns a query request from a dict of field/values

                Parameters:
                        fields (dict): A dictionary of fields with values

                Returns:
                        request (QuerySecurityRequest): A request wrapper, with the fields attached. It assumes that all filters are an equals operation
        '''

        filters = []

        for field in fields:
            field:FieldProto

            field_value = fields[field]            

            packed_value:Any = Any()
            packed_value.Pack(msg=field_value)

            entry = FieldMapEntry(
                field=field, field_value_packed=packed_value
            )

            filters.append(entry)
        

        as_of_proto:LocalTimestampProto = ProtoSerializationUtil.serialize(datetime.now())

        request:QuerySecurityRequestProto = QuerySecurityRequestProto(
            search_security_input=PositionFilterProto(
                filters=filters
            ),
            as_of=as_of_proto
        )

        return QuerySecurityRequest(proto=request)
    
    def __init__(self, proto:CreateSecurityRequestProto):
        self.proto = proto