import os, sys
import json
import logging
import traceback
from uuid import uuid4, UUID
from datetime import datetime, timedelta

from abc import ABCMeta, abstractmethod
from harvest.controllers.project_controller import ProjectController
from harvest.controllers.project_user_controller import ProjectUserController
from harvest.controllers.notification_controller import NotificationController
from harvest.drivers.cognito_driver import CognitoDriver

DYNAMO_HOST = os.environ.get("DYNAMO_HOST")
DYNAMO_PORT = os.environ.get("DYNAMO_PORT")
COGNITO_USER_POOL_ID="ap-northeast-1_KrEPljcrG"
ADMINS = ["owner", "admin"]
CLEANUP_DAYS = 14

class BaseNotification():
  __stream_record = {}
  create = "{project_name}: {user_name}さんが{type}:{name}を追加しました"
  update = "{project_name}: {user_name}さんが{type}:{old_name}を{name}に変更しました"
  delete = "{project_name}: {user_name}さんが{type}:{name}を削除しました"
  message = ""
  needs_strings_dict = {"project_name": "", "user_name": "", "type": ""}
  is_insert_event = False
  is_modify_event = False
  is_remove_event = False
  record = {}
  old_record = None
  notify_users = []
  formatter = logging.Formatter('[%(levelname)s]\t%(asctime)s.%(msecs)dZ\t%(aws_request_id)s\t[%(module)s#%(funcName)s %(lineno)d]\t%(message)s')
  logger = logging.getLogger()

  def __init__(self, stream_record, host, port):
    self.project = ProjectController(host, port)
    self.users = ProjectUserController(host, port)
    self.cognito = CognitoDriver(COGNITO_USER_POOL_ID)
    self.set_stream_record(stream_record)

    for handler in self.logger.handlers:
      handler.setFormatter(self.formatter)
      handler.setLevel(logging.DEBUG)

    self.logger = logging.getLogger(__name__)
    self.logger.setLevel(logging.DEBUG)

    self.logger.info("processing stream_record: %s" % str(stream_record))
    event_name = stream_record["eventName"]
    self.logger.info("happened event name: %s" % str(event_name))
    if event_name == "INSERT":
      self.is_insert_event = True
      self.record = stream_record["dynamodb"]["NewImage"]

    elif event_name == "MODIFY":
      self.is_modify_event = True
      self.record = stream_record["dynamodb"]["NewImage"]
      self.old_record = stream_record["dynamodb"]["OldImage"]

    elif event_name == "REMOVE":
      self.is_remove_event = True
      self.record = stream_record["dynamodb"]["OldImage"]

    self.project_id = self.record["project_id"]
    self.user_id = self.record["updated_by"]
    self.logger.info("using project_id: %s" % self.project_id)
    self.logger.info("using user_id: %s" % self.user_id)

  def set_stream_record(self, record):
    self.record = record
    self.users.set_project_id(record["dynamodb"]["Keys"]["project_id"])

  def __get_project_name(self):
    try:
      project = self.project.show(
          self.project_id
      )
    except Exception as e:
      project = {"name": "【不明】"}

    return project["name"]

  def __get_user_name(self):
    try:
      detail = self.cognito.show_user(self.user_id)[0]["Attributes"]
      detail = { item["Name"]: item["Value"] for item in detail }
    except Exception as e:
      detail = {"preferred_username": "【不明】"}

    return detail["preferred_username"]

  def select_message(self):
    if self.is_insert_event:
      self.message = self.create

    elif self.is_modify_event:
      self.message = self.update

    elif self.is_remove_event:
      self.message = self.delete
    return self.message

  def mapping(self):
    self.message = self.message.format_map(
      self.needs_strings_dict
    )

  def set_record_type(self, record_type):
    self.needs_strings_dict["type"] = record_type

  def get_notify_users(self, target, user_id=None):
    if target not in ["all", "admins", "specify"]:
      # TODO: needs custom exception
      raise ValueError
    self.logger.info("target notify type: %s" % str(target))

    project_users = self.users.list()
    project_users = [ user for user in project_users if user["status"] == "active" ]

    self.logger.info("candidate target notify users: %s" % str(project_users))
    # TODO: needs delete myself user_id
    if target == "all":
      project_users = [ user for user in project_users if user["user_id"] != self.user_id ]
    elif target == "admins":
      #filterd_users = filter(lambda user: user["role"] in ADMINS, project_users)
      #project_users = [user for user in filterd_users]
      project_users = [ user for user in project_users if user["role"] in ADMINS and user["user_id"] != self.user_id ]
    elif target == "specify":
      if not user_id:
        # TODO: needs custom exception
        raise ValueError
      project_users = [ {"user_id": user_id} ]

    self.logger.info("processing done. notify users: %s" % str(project_users))
    return project_users

  def generate(self):
    if not self.message:
      self.select_message()

    ret = []
    if self.message:
      if not self.notify_users:
        self.notify_users = self.get_notify_users("all")
      self.needs_strings_dict["project_name"] = self.__get_project_name()
      self.needs_strings_dict["user_name"] = self.__get_user_name()
      self.logger.info("needs mapping string dictionary: %s" % str(self.needs_strings_dict))

      self.mapping()
      new_id = str(uuid4())

      iso_now = datetime.now().isoformat()
      now = datetime.now() + timedelta(days=CLEANUP_DAYS)
      ttl = int(now.timestamp())
      for user in self.notify_users:
        ret.append(
          {
            "user_id": user["user_id"],
            "notification_id": new_id,
            "project_id": self.project_id,
            "created_at": iso_now,
            "updated_at": iso_now,
            "message": self.message,
            "read": False,
            "ttl": ttl
          }
        )
    return ret
