# AUTOGENERATED! DO NOT EDIT! File to edit: pynamodbAttributes.ipynb (unless otherwise specified).

__all__ = ['SchemaAttribute', 'extraDocsString', 'SuperModel', 'createData', 'getData', 'updateData']

# Cell
from pynamodb.attributes import Attribute, UnicodeAttribute, NumberAttribute
from typing import Any, Optional, Type, TypeVar
from enum import Enum
import requests, dpath.util, yaml, jsonschema, json, os, pynamodb, pytest
from pynamodb.models import Model
from datetime import datetime
from awsSchema.apigateway import Event, Response
from jsonschema import ValidationError
from typing import Optional
from .schema import validateUrl
from beartype import beartype

# Cell
class SchemaAttribute(Attribute):
  attr_type = pynamodb.constants.STRING
  def __init__(self, schemaUrl:str, path:str = '/', isYaml=True,
               headers={'Cache-Control': 'no-cache'},
               envName = 'SCHEMA_ATTRIBUTE', **kwargs: Any) -> None:
      """
      schemaUrl:str,
      path:str = '/',
      isYaml=True,  :yaml::Bool:: whether the schema is in yaml or json
      headers={'Cache-Control': 'no-cache'},
      :path::str:: the path of the object of interest in schema, if the schema is at root then '/'
      envName::str:: the name of schema to save to the environment
      """
      super().__init__(**kwargs)
      try:
        if isYaml: # yaml schema
          schema:dict = yaml.load(requests.get(schemaUrl, headers=headers).text, Loader = yaml.FullLoader)
        else: # probably json
          schema:dict = requests.get(schemaUrl, headers).json()
      except Exception as e:
        print(f'error parsing schema {e}')
        schema:dict = {}

      self.schema = dpath.util.get(schema, path) # get to the path in schema
      os.environ[envName] = json.dumps(self.schema)

  def deserialize(self, value: str) -> dict:
    return json.loads(value)

  def serialize(self, value:dict) -> str:
    res = jsonschema.validate(value,self.schema)
    return json.dumps(value)

# Cell
extraDocsString = '''
  def pullOutKeys(self):
    self.id_ = self.data['id']
  def update(self, inputDict:dict):
    self.data.update(inputDict)
  def fromDict(cls, inputDict:dict):
    return cls(data = inputDict)
  def toDict(self):
    return self.data

'''

# Cell
class SuperModel(Model):
  f'''
  a model intended to use as a standard single json object saving
  please override functions including
  - fromDict
  - toDict
  - pullOutKeys
  - update
  override as necessary for example
  {extraDocsString}
  '''
  #id_ = UnicodeAttribute(hash_key=True)
  data = SchemaAttribute(schemaUrl='https://gist.githubusercontent.com/thanakijwanavit/e2720d091ae0cef710a49b57c0c9cd4c/raw/ed2d322eac4900ee0f95b431d0f9067a40f3e0f0/squirrelOpenApiV0.0.3.yaml', null=True)
  lastEdited = NumberAttribute()
  creationTime = NumberAttribute()

  def __repr__(self):
    return json.dumps(vars(self)['attribute_values'])

  ## query override
  @classmethod
  def queryId(cls, hash_key, **kwargs):
    '''
    just like query but make resul
    '''
    r = cls.query(hash_key, **kwargs)
    res = []
    for i in r:
      i.pullOutKeys()
      res.append(i)
    return iter(res)


  ## moving to and from dict
  @classmethod
  def fromDict(cls, inputDict:dict):
    '''
    turn dict into class
    note that this assume data as the only column, please override if required
    '''
    return cls(data = inputDict)

  def toDict(self):
    '''
    turn class into a dictionary
    note that this is set to return self.data by default, please override if needed
    '''
    return self.data

  #### saving ####
  def pullOutKeys(self):
    '''
    update the keys with data: please override this function by pulling out keys

    for example
    self.orderId = self.data['orderId']
    self.ownerId = self.data['ownerId']
    self.basketId = self.data['basketId']
    '''
    print('please dont foreget to override the pullOutKeys function if needed')
    self.id_ = self.data['id']

  def recordTime(self):
    '''record last edited and creation time'''
    self.lastEdited = datetime.now().timestamp() # record last edited
    if not self.creationTime: # record creation time
      self.creationTime = datetime.now().timestamp()

  def save(self):
    '''
    please override pullOutKeys function
    see docs
    this function performs following before saving the record
      1. self.recordTime()
      2. self.pullOutKeys()
    '''
    self.recordTime()
    self.pullOutKeys()

    try:
      super().save()
      return self
    except ValidationError as e:
      raise ValidationError(f'failed validation \n {e}')
    except Exception as e:
      raise Exception(f'error saving id  {e}')
  def update(self, inputDict:dict):
    '''
    update with dictionary input
    please override as necessary
    '''
    self.data.update(inputDict)


# Cell
def createData(event:dict, hashKeyName: str,mainClass:Model, schemaUrl:Optional[str] = None ,schemaFormat:str ='yaml', *args):
  '''
    create a new row of data
  '''
  # parse output
  query:dict = Event.parseBody(event)

  # check schema if provided
  if schemaUrl:
    try: validateUrl(schemaUrl,format_ = schemaFormat)
    except ValidationError as e: return Response.returnSuccess(f'{e}')

  # check for key
  if hashKeyName not in query:
    return Response.returnError(message=f'missing {hashKeyName}')

  # check if object exist
  if next(mainClass.query(query[hashKeyName]),None):
    return Response.returnError(message=f'item with the same hash key exists')

  # make pynamodb object
  item:mainClass = mainClass.fromDict(query)

  # try to save
  try:
    item.save()
    return Response.returnSuccess(body=item.toDict())

  except ValidationError as e: # error validation handle
    return Response.returnError(f'validation error \n {e}')

  except Exception as e: # error handle
    return Response.returnError(f'unknown error \n {e} \n errorString())')

# Cell
from awsSchema.apigateway import Event, Response
from jsonschema import ValidationError
from typing import Optional
from .schema import validateUrl

def getData(hashKeyName:str, mainClass: Model):
  '''
    create a new basket
  '''
  # get data
  try:
    r:Optional[Model] = next(mainClass.query(hashKeyName), None)
  except Exception as e:
    return Response.returnError(f'failed to query with error {e}')

  # product not found
  if not r: return Response.returnError(f'not found')
  #success
  else: return Response.returnSuccess(r.toDict())

# Cell
def updateData(event:dict, hashKeyName: str,mainClass:Model,
               schemaUrl:Optional[str] = None ,schemaFormat:str ='yaml', *args):
  '''
  updating data based on the new input

  event:dict: object gathered from apiGatewayProxy
  hashKeyName:str: the name of the hash key
  mainClass:Model: pynamodb model class for this object
  schemaUrl:Optional[str]: url of the input schema for validation
  schemaFormat:Enum['yaml', 'json']
  '''
  # parse output
  query:dict = Event.parseBody(event)

  # check schema if provided
  if schemaUrl:
    try: validateUrl(schemaUrl,format_ = schemaFormat)
    except ValidationError as e: return Response.returnSuccess(f'{e}')

  # check for key
  if hashKeyName not in query:
    return Response.returnError(message=f'missing {hashKeyName}')

  # check if object exist
  item:mainClass = next(mainClass.query(query[hashKeyName]),None)
  if not item:
    return Response.returnError(message=f'item with the same hash key doesnt exist')

  # update the data
  item.update(query)

  # try to save
  try:
    item.save()
    return Response.returnSuccess(body=item.toDict())

  except ValidationError as e: # error validation handle
    return Response.returnError(f'validation error \n {e}')

  except Exception as e: # error handle
    return Response.returnError(f'unknown error \n {e} \n errorString())')
