#!/usr/bin/env python
import os
from autosubmitAPIwu.config.basicConfig import BasicConfig
from autosubmitAPIwu.components.jobs.job_factory import SimJob, Job
from autosubmitAPIwu.config.config_common import AutosubmitConfig
from bscearth.utils.config_parser import ConfigParserFactory
from autosubmitAPIwu.job.job_utils import datechunk_to_year
from abc import ABCMeta, abstractmethod
from autosubmitAPIwu.common.utils import JobSection, parse_number_processors, timestamp_to_datetime_format
from typing import List

class ProjectType:
  GIT = "git"
  SVN = "svn"

class ConfigurationFacade:
  """ """
  __metaclass__ = ABCMeta
  
  def __init__(self, expid, basic_config):
    # type: (str, BasicConfig) -> None
    self.basic_configuration = basic_config # type: BasicConfig    
    self.expid = expid # type: str    
    self.experiment_path = "" # type: str
    self.pkl_path = "" # type: str
    self.tmp_path = "" # type: str
    self.log_path = "" # type: str
    self.structures_path = "" # type: str
    self.chunk_unit = "" # type: str
    self.chunk_size = "" # type: int
    self.current_years_per_sim = 0.0 # type: float
    self.sim_processors = 0  # type: int
    self.experiment_stat_data = None # type: os.stat_result
    self.warnings = [] # type: List[str] 
    self._process_basic_config()

  def _process_basic_config(self):
    # type: () -> None
    pkl_filename = "job_list_{0}.pkl".format(self.expid)    
    self.experiment_path = os.path.join(self.basic_configuration.LOCAL_ROOT_DIR, self.expid)
    self.pkl_path = os.path.join(self.basic_configuration.LOCAL_ROOT_DIR, self.expid, "pkl", pkl_filename)
    self.tmp_path = os.path.join(self.basic_configuration.LOCAL_ROOT_DIR, self.expid, self.basic_configuration.LOCAL_TMP_DIR)
    self.log_path = os.path.join(self.basic_configuration.LOCAL_ROOT_DIR, self.expid, "tmp", "LOG_{0}".format(self.expid))
    self.structures_path = self.basic_configuration.STRUCTURES_DIR
    if not os.path.exists(self.experiment_path): raise Exception("Experiment folder {0} not found".format(self.experiment_path))
    if not os.path.exists(self.pkl_path): raise Exception("Required file {0} not found.".format(self.pkl_path))
    if not os.path.exists(self.tmp_path): raise Exception("Required folder {0} not found.".format(self.tmp_path))
      
  @abstractmethod
  def _process_advanced_config(self):
    pass
  
  @abstractmethod
  def get_autosubmit_version(self):
    pass
  
  @abstractmethod
  def _get_processors_number(self, conf_sim_processors):
    # type: (str) -> int
    pass

  @abstractmethod
  def get_model(self):
    # type: () -> str
    pass

  @abstractmethod
  def get_branch(self):
    # type: () -> str
    pass
  
  @abstractmethod
  def get_owner_name(self):
    # type: () -> str
    pass
  
  @abstractmethod
  def get_owner_id(self):
    # type: () -> int
    pass

class BasicConfigurationFacade(ConfigurationFacade):
  """ BasicConfig and paths """
  def __init__(self, expid, basic_config):
    # type: (str, BasicConfig) -> None
    super(BasicConfigurationFacade, self).__init__(expid, basic_config)
  
  def _process_advanced_config(self):
    raise NotImplementedError
  
  def get_autosubmit_version(self):
    raise NotImplementedError
  
  def _get_processors_number(self, conf_sim_processors):
    raise NotImplementedError
  
  def get_model(self):
    raise NotImplementedError
  
  def get_branch(self):
    raise NotImplementedError

  def get_owner_name(self):
    raise NotImplementedError
  
  def get_owner_id(self):
    raise NotImplementedError

class AutosubmitConfigurationFacade(ConfigurationFacade):
  """ Autosubmit Configuration includes Basic Config """
  def __init__(self, expid, basic_config, autosubmit_config):
    # type: (str, BasicConfig, AutosubmitConfig) -> None
    super(AutosubmitConfigurationFacade, self).__init__(expid, basic_config)
    self.autosubmit_conf = autosubmit_config
    self._process_advanced_config()
  
  def _process_advanced_config(self):
    """ Advanced Configuration from AutosubmitConfig """
    # type: () -> None
    # self.autosubmit_conf = autosubmit_config # AutosubmitConfig(self.expid, BasicConfig, ConfigParserFactory())    
    self.autosubmit_conf.reload()    
    self.chunk_unit = self.autosubmit_conf.get_chunk_size_unit()
    self.chunk_size = self.autosubmit_conf.get_chunk_size()    
    self.current_years_per_sim = datechunk_to_year(self.chunk_unit, self.chunk_size)    
    self.sim_processors = self._get_processors_number(self.autosubmit_conf.get_processors(JobSection.SIM))
    self.experiment_stat_data = os.stat(self.experiment_path)    
  
  def get_pkl_last_modified_timestamp(self):
    # type: () -> int
    return int(os.stat(self.pkl_path).st_mtime)

  def get_experiment_last_access_time_as_datetime(self):
    # type: () -> str
    return timestamp_to_datetime_format(int(self.experiment_stat_data.st_atime))
  
  def get_experiment_last_modified_time_as_datetime(self):
    # type: () -> str
    return timestamp_to_datetime_format(int(self.experiment_stat_data.st_mtime))

  def get_owner_id(self):
    # type: () -> int    
    return int(self.experiment_stat_data.st_uid)

  def get_owner_name(self):
    # type: () -> str    
    owner_name = os.popen("id -nu {0}".format(str(self.get_owner_id()))).read().strip()    
    return str(owner_name)

  def get_autosubmit_version(self):
    # type: () -> str
    return self.autosubmit_conf.get_version()

  def get_main_platform(self):
    return str(self.autosubmit_conf.get_platform())

  def get_section_processors(self, section_name):
    # type: (str) -> int
    return self._get_processors_number(str(self.autosubmit_conf.get_processors(section_name)))
  
  def get_section_qos(self, section_name):
    return str(self.autosubmit_conf.get_queue(section_name))

  def get_section_platform(self, section_name):
    return str(self.autosubmit_conf.get_job_platform(section_name))

  def get_platform_qos(self, platform_name, number_processors):
    # type: (str, int) -> str    
    if number_processors == 1:
      qos = str(self.autosubmit_conf.get_platform_serial_queue(platform_name))
      if len(qos.strip()) > 0:
        return qos
    return str(self.autosubmit_conf.get_platform_queue(platform_name))

  def get_wrapper_qos(self):
    return str(self.autosubmit_conf.get_wrapper_queue())

  def get_section_wallclock(self, section_name):
    return str(self.autosubmit_conf.get_wallclock(section_name))
  
  def get_platform_max_wallclock(self, platform_name):
    return str(self.autosubmit_conf.get_platform_wallclock(platform_name))
  
  def get_safety_sleep_time(self):
    # type: () -> int
    return self.autosubmit_conf.get_safetysleeptime()
  
  def get_project_type(self):
    # type: () -> str
    return self.autosubmit_conf.get_project_type()  

  def get_model(self):
    # type: () -> str
    if self.get_project_type() == ProjectType.GIT:
      return self.get_git_project_origin()
    elif self.get_project_type() == ProjectType.SVN:
      return self.get_svn_project_url()
    else:
      return "NA"
  
  def get_branch(self):
    # type: () -> str
    if self.get_project_type() == ProjectType.GIT:
      return self.get_git_project_branch()
    elif self.get_project_type() == ProjectType.SVN:
      return self.get_svn_project_url()
    else:
      return "NA"

  def get_git_project_origin(self):
    # type: () -> str
    return self.autosubmit_conf.get_git_project_origin()
  
  def get_git_project_branch(self):
    # type: () -> str
    return self.autosubmit_conf.get_git_project_branch()

  def get_svn_project_url(self):
    # type: () -> str
    return self.autosubmit_conf.get_svn_project_url()

  def update_sim_jobs(self, sim_jobs):
    # type: (List[SimJob]) -> None
    """ Update the jobs with the latest configuration values: Processors, years per sim """        
    for job in sim_jobs:
      job.set_ncpus(self.sim_processors)
      job.set_years_per_sim(self.current_years_per_sim)      
  
  def _get_processors_number(self, conf_job_processors):
    # type: (str) -> int
    num_processors = 0
    try:
        if str(conf_job_processors).find(":") >= 0:            
            num_processors = parse_number_processors(conf_job_processors)
            self._add_warning("Parallelization parsing | {0} was interpreted as {1} cores.".format(
                conf_job_processors, num_processors))
        else:
            num_processors = int(conf_job_processors)
    except:        
        self._add_warning(
            "CHSY Critical | Autosubmit API could not parse the number of processors for the SIM job.")
        pass        
    return num_processors
  
  def _add_warning(self, message):
    # type: (str) -> None
    self.warnings.append(message)
  



    





