import sys
import os

sys.path.append(os.path.join(os.path.dirname(__file__), 'generated'))


import grpc
import generated.calculator_pb2 as calculator_pb2
import generated.calculator_pb2_grpc as calculator_pb2_grpc
import threading

class ClientGateway:
    def __init__(self):
        self.addition_channel = grpc.insecure_channel('localhost:50052')
        self.multiplication_channel = grpc.insecure_channel('localhost:50053')
        self.addition_stub = calculator_pb2_grpc.CalculatorStub(self.addition_channel)
        self.multiplication_stub = calculator_pb2_grpc.CalculatorStub(self.multiplication_channel)
    
    def perform_operations(self, a, b):
        """Send numbers to both addition and multiplication services"""
        try:
            addition_thread = threading.Thread(target=self._call_addition, args=(a, b))
            multiplication_thread = threading.Thread(target=self._call_multiplication, args=(a, b))
            
            addition_thread.start()
            multiplication_thread.start()
            
            addition_thread.join()
            multiplication_thread.join()
            
        except grpc.RpcError as e:
            print(f"Error: {e.code()} - {e.details()}")
    
    def _call_addition(self, a, b):
        """Call addition service"""
        try:
            numbers = calculator_pb2.Numbers(a=a, b=b)
            response = self.addition_stub.Add(numbers)
            print(f" Addition: {a} + {b} = {response.value}")
        except grpc.RpcError as e:
            print(f"Addition Service Error: {e.details()}")
    
    def _call_multiplication(self, a, b):
        """Call multiplication service"""
        try:
            numbers = calculator_pb2.Numbers(a=a, b=b)
            response = self.multiplication_stub.Multiply(numbers)
            print(f"  Multiplication: {a} × {b} = {response.value}")
        except grpc.RpcError as e:
            print(f"Multiplication Service Error: {e.details()}")
    
    def get_history(self):
        """Request operation history from both services"""
        print("\n Fetching operation history...")
        self._get_addition_history()
        self._get_multiplication_history()
    
    def _get_addition_history(self):
        """Get addition operation history"""
        try:
            response_stream = self.addition_stub.GetOperationHistory(calculator_pb2.Empty())
            print("\nAddition Operation History:")
            for history in response_stream:
                for op in history.operations:
                    print(f"  {op.timestamp}: {op.a} + {op.b} = {op.result}")
        except grpc.RpcError as e:
            print(f"Error getting addition history: {e.details()}")
    
    def _get_multiplication_history(self):
        """Get multiplication operation history"""
        try:
            response_stream = self.multiplication_stub.GetOperationHistory(calculator_pb2.Empty())
            print("\nMultiplication Operation History:")
            for history in response_stream:
                for op in history.operations:
                    print(f"  {op.timestamp}: {op.a} × {op.b} = {op.result}")
        except grpc.RpcError as e:
            print(f"Error getting multiplication history: {e.details()}")

def main():
    client = ClientGateway()
    
    while True:
        print("\n" + "="*50)
        print(" Distributed Calculator System")
        print("1. Perform calculations")
        print("2. Show operation history")
        print("3. Exit")
        
        choice = input("Choose option: ")
        
        if choice == "1":
            try:
                a = int(input("Enter first number: "))
                b = int(input("Enter second number: "))
                client.perform_operations(a, b)
            except ValueError:
                print(" Please enter valid integers only!")
        
        elif choice == "2":
            client.get_history()
        
        elif choice == "3":
            print("Goodbye!")
            break
        
        else:
            print(" Invalid choice!")

if __name__ == "__main__":
    main()