"""_____________________________________________________________________

:PROJECT: LARAsuite

*loading LARA projects and experiments from YAML file*

:details: loading LARA projects and experiments from YAML file, using pydantic models
         - 
:authors: mark doerr <mark.doerr@uni-greifswald.de>

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


import os
import sys
import yaml
import uuid
import grpc
import logging
from pathlib import Path
from typing import List, Optional
from datetime import datetime
from pydantic import BaseModel, ValidationError, field_validator, root_validator, constr, Field

import lara_django_base_grpc.v1.lara_django_base_pb2 as lara_django_base_pb2
import lara_django_base_grpc.v1.lara_django_base_pb2_grpc as lara_django_base_pb2_grpc

import lara_django_projects_grpc.v1.lara_django_projects_pb2 as lara_django_proj_pb2
import lara_django_projects_grpc.v1.lara_django_projects_pb2_grpc as lara_django_proj_pb2_grpc


def generate_name(title: str = None) -> str:
    """
    generate name from title
    """
    if title is not None:
        return title.lower().replace(" ", "_")
    else:
        return f"{datetime.now().strftime('%Y%m%d%H%M%S')}_project"

# LARAExperiment

class LARAExperiment(BaseModel):
    namespace : Optional[str] = Field(description="namespace of the experiment", default=None)
    name : str = Field(..., description="name of the experiment")
    title : Optional[str] = Field(Optional, description="title of the experiment")
    parent : Optional[str] = Field(description="parent experiment", default=None)
    project: Optional[str] = Field(description="project of the experiment", default=None)
    description : Optional[str] = Field(description="description of the experiment", default=None)
    datetime_start : Optional[datetime] = Field(description="start datetime of the experiment", default=datetime.now())

    @field_validator("name", mode="before")
    def _clean_name(cls, name):
        return name.lower().replace(" ", "_")
    
    @field_validator("title", mode="before")
    def _default_title(cls, title, name):
        if title is None:
            return name

# use the fields from LARAProject and LARAExperiment to create the pydantic models, s. lara-django-projects/lara_django_projects/models.py
class LARAProject(BaseModel):
    namespace : Optional[str] = Field(description="namespace of the project", default=None)
    name : str = Field(..., description="name of the project")
    title : Optional[str] = Field(Optional, description="title of the project")
    parent : Optional[str] = Field(description="parent project", default=None)
    description : Optional[str] = Field(description="description of the project", default=None)
    experiments : Optional[List[LARAExperiment]] = Field(Optional, description="list of experiments")

    @field_validator("name", mode="before")
    def _clean_name(cls, name):
        return name.lower().replace(" ", "_")
    
    @field_validator("title", mode="before")
    def _default_title(cls, title, name):
        if title is None:
            return name


class LARAnamespace(BaseModel):
    urn : str = Field(..., description="urn of the namespace")
    prefix : str = Field(..., description="prefix of the namespace")
    default : bool = Field(Optional, description="default namespace")
    description: Optional[str] = Field(Optional, description="description of the namespace")
    
    @field_validator("urn", mode="before")
    def _clean_urn(cls, urn):
       # add a urn validator here:
       return urn.lower().replace(" ", "_")
       
class LARAProjectStructure(BaseModel):
    projects: List[LARAProject] = Field(..., description="list of projects")
    namespaces: Optional[List[LARAnamespace]] = Field(Optional, description="list of namespaces")

class LARAProjectsLoader:
    """
    load projects from YAML file
    """

    def __init__(self, yaml_file: str):
        self.yaml_file = yaml_file
        self.projects = []

        self.logger = logging.getLogger(__name__)

        grpc_server_host = os.getenv("GRPC_SERVER", default='localhost')  # '127.0.0.1' 
        grpc_server_port = os.getenv("GRPC_PORT", default=50051)  

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

        # dedicated gRPC clients for each LARA gRPC service
        self.namespace_client = lara_django_base_pb2_grpc.NamespaceControllerStub(lara_channel)
        self.proj_client = lara_django_proj_pb2_grpc.ProjectControllerStub(lara_channel)        
        
    def load(self):
        """
        load projects from YAML file
        """
        self.logger.info(f"loading projects from {self.yaml_file}")
        with open(self.yaml_file, "r") as stream:
            try:
                projects = yaml.safe_load(stream)
                self.project_struc = LARAProjectStructure(**self.projects)
                self.projects = self.project_struc.projects
                self.namespaces = self.project_struc.namespaces

                self.logger.info(f"loaded {len(self.projects)} projects")
                self.logger.debug(f"projects: {self.projects}")
                self.logger.debug(f"namespaces: {self.namespaces}")
            except yaml.YAMLError as exc:
                self.logger.error(exc)
    
    def create_projects(self):
        """
        create project
        """

        # check, if namespace exists in database, otherwise create it



        # iterate over projects, check, if parent exists in database, otherwise create it

        parents_dict = {}

        for project in self.projects:
            if project.parent is not None:
                # check, if parent exists in database

                parents_dict[project.parent] = project.parent

        

        # check, if project exists in database, otherwise create it

        #logger.info(f"creating project {project.name}")
        # create project


        # return lara_django_projects_pb2.Project(
        #     namespace=project.namespace,
        #     name=project.name,
        #     title=project.title,
        #     parent=project.parent,
        #     description=project.description,
        #     datetime_start=project.datetime_start,
        # )
            
            
def main():
    """
    main function
    """
    # command line arguments parser

    import argparse
    parser = argparse.ArgumentParser(description="load projects from YAML file")
    parser.add_argument("-f", "--file", type=str, help="YAML file with projects", default="projects.yaml")
    args = parser.parse_args()

    lpl = LARAProjectsLoader(args.file)
    lpl.load()

    ## pretty print projects

    print("projects:", lpl.projects)


if __name__ == "__main__":
    sys.exit(main())

