# -*- coding: utf-8 -*-
"""_____________________________________________________________________

:PROJECT: LARAsuite

*generate gRPC client *

:details: generate_grpc_client - a gRPC client generator for LARA-django.
          This is a LARA-django-base management command to generate a gRPC client for a
          given app. The generated client is a python module that can be used to
          communicate with the gRPC server of the given app.

 - `fields` for all local fields

:authors: mark doerr <mark.doerr@uni-greifswald.de>

.. note:: -
.. todo:: - 
________________________________________________________________________
"""

import re
import os
import logging

from django.apps import apps
from django import models
from django.conf import settings
from django.core.management.base import LabelCommand, CommandError
from django.db import models


# Configurable constants
MAX_LINE_WIDTH = getattr(settings, 'MAX_LINE_WIDTH', 120)
INDENT_WIDTH = getattr(settings, 'INDENT_WIDTH', 4)

CLIENT_FILE_HEADER = """""""""_____________________________________________________________________

:PROJECT: LARAsuite

*{lara_app} gRPC client *

:details: {lara_app} client.
         - generated with 'lara-django-dev generate_grpc_client {lara_app}
:authors: {lara_author} <{lara_author_email}>

.. note:: -
.. todo:: - 
________________________________________________________________________
"""""""""



class Command(LabelCommand):
    help = '''Generate a gRPC client for a given LARA app (models)'''
    args = "[app_name, author_name, author_email]"
    can_import_settings = True

    def add_arguments(self, parser):
        parser.add_argument('app_name', help='Name of the LARA app to generate the gRPC interface for')
            
        parser.add_argument('--author-name', action="store_true", help='Name of the author',  default='lara')
        parser.add_argument('--author-email', action="store_true", help='Email of the author', default='lara@lara.org')
        #parser.add_argument('model_name', nargs='*')

    #@signalcommand
    def handle(self, *args, **options):
        self.app_name = options['app_name']

        logging.warning("!! only a scaffold is generated, please check/add content to the generated files !!!")

        try:
            app = apps.get_app_config(self.app_name)
        except LookupError:
            self.stderr.write('This command requires an existing app name as argument')
            self.stderr.write('Available apps:')
            app_labels = [app.label for app in apps.get_app_configs()]
            for label in sorted(app_labels):
                self.stderr.write('    %s' % label)
            return

        model_res = []
        # for arg in options['model_name']:
        #     model_res.append(re.compile(arg, re.IGNORECASE))

        GRPC_ClientApp(app, model_res, options)

        #self.stdout.write()

class GRPC_ClientApp():
    def __init__(self, app_config, model_res, options):
        self.app_config = app_config
        self.model_res = model_res
       
        self.app_name = options['app_name']
        self.author_name = options['author_name']
        self.author_email = options['author_email']

        self.client_str = ""
        self.model_names = [model.__name__ for model in self.app_config.get_models()]

        self.generate_client()

    def get_relational_keys(self, model):
        relational_keys = []
        for field in model._meta.fields:
            if isinstance(self._meta.get_field_by_name(field.name)[0], models.ForeignKey):
                relational_keys.append(field.name)
            if isinstance(self._meta.get_field_by_name(field.name)[0], models.ManyToManyKey):
                relational_keys.append(field.name)
            if isinstance(self._meta.get_field_by_name(field.name)[0], models.OneToOneKey):
                relational_keys.append(field.name)
        if not relational_keys:
            return None
        return relational_keys

    def generate_client_imports(self):
        self.client_str += CLIENT_FILE_HEADER.format(
            lara_app=self.app_name,
            lara_author=self.author_name,
            lara_author_email=self.author_email,
        )
        self.client_str += f"""

import logging
import asyncio

# import api
#import {self.app_name}.grpc.{self.app_name}_pb2 as {self.app_name}_pb2

from {self.app_name}.models import {', '.join(self.model_names)}\n\n"""

    def generate_client(self):
        self.generate_client_imports()

        grpc_handler_str = ""
        grpc_handler_create_str = ""
        foreing_keys = []

        for model in self.app_config.get_models():
            fields = [field.name for field in model._meta.fields if "_id" not in field.name]
            relational_keys = self.get_relational_keys(model)
            # :TODO: use set compreshension
            non_rel_fields = fields - relational_keys
            #fields_param_str = ", ".join([f"{field}=None" for field in fields])
            fields_str = " ,".join([f"\n{4 * INDENT_WIDTH * ' '}{field}=" for field in fields])
            #fields_str = ",".join([f"\n{4 * INDENT_WIDTH * ' '}'{field}'" for field in fields])
            #fields_str = ", ".join([f"{field}'" for field in fields])
            

            grpc_handler_str += f"""{ model.__name__.lower()}_handler = {model.__name__.capitalize()}GRPCHandler(channel)\n"""
            grpc_handler_create_str += f"""{ model.__name__.lower()}_handler.create_{model.__name__.lower()}(n=1)\n"""

            self.client_str += f"""class {model.__name__.capitalize()}GRPCHandler():
    def __init__(self, channel):
        
        # create a client (stub)
        self.{model.__name__.lower()}_client = {self.app_name}_pb2.{self.app_name}Stub(channel)
         
    def create_{model.__name__.lower()}_entries(self, n=1):

        for i in range(n):
            res = self.{model.__name__.lower()}_client.Create({self.app_name}_pb2.{model.__name__.capitalize()}Request({fields_str}))
        \n\n
        
        # proto_class = {self.app_name}_pb2.{model.__name__.capitalize()}Response \n
        # proto_class_list = {self.app_name}_pb2.{model.__name__.capitalize()}ListResponse \n\n"""


        # main section

        self.client_str += f"""
async def main():
    # create a channel to a grpc server
    grpc_server_host = 'localhost' # '127.0.0.1' 
    grpc_server_port = 50066

    channel = grpc.aio.insecure_channel(f'{{grpc_server_host}}:{{grpc_server_port}}')

    {grpc_handler_str}\n
    # calling the create method of the stub for the given model
    {grpc_handler_create_str}\n
    

if __name__ == "__main__":
    asyncio.run(main())
"""

        client_filename = f"{self.app_name}_client.py"

        print(f"writing {client_filename} to {os.getcwd()}")
        print(self.client_str)

        # if os.path.isfile(client_filename):
        #     append = input(f"{client_filename} already exists, append to file? (y/n) ")
        #     if append.lower() == "y":
        #         with open(client, "a") as f:
        #             f.write(self.client_str)
        # else:
        #     # write sef.client_str to file
        #     with open(client, "w") as f:
        #         f.write(self.client_str)
