import os, sys
import logging
from datetime import datetime
from base64 import b64decode
from uuid import uuid4
import chardet

from harvest.drivers.dynamodb_driver import DynamoDbDriver
from harvest.utils.csv_optimizer import CsvOptimizer
from harvest.controllers.place_controller import PlaceController
from harvest.controllers.target_controller import TargetController
from harvest.exception import ActionDeniedError, CsvContentDecodeError

# カスタム例外も足して投げられるようにする
#TODO: api gateway側にも実装
# guiなしでの動作確認
# webui側の実装

class ImportController():
  places = None
  targets = None
  project_id = None

  logger = logging.getLogger(__name__)
  logger.addHandler(logging.NullHandler())
  logger.setLevel(logging.DEBUG)

  def __init__(self, host, port, filename=None):
    self.place_controller = PlaceController(host, port)
    self.target_controller = TargetController(host, port)

    if filename:
      self.import_csv(filename)

  def set_project_id(self, project_id):
    self.project_id = project_id

  # csvを読み込む
  def open_csv(self, filename, workspace="/tmp", base64enc=False):
    #TODO: base64ではなくファイル読み込みのときに置換されない
    if base64enc:
      csv_file = os.path.join(workspace, str(uuid4()) + ".csv")

      self.logger.info("base64 encoded content: {}".format(filename))
      decoded = b64decode(filename)
      self.logger.info("base64 decoded content: {}".format(decoded))

      char_enc = chardet.detect(decoded)
      self.logger.info("detect character encode: {}".format(char_enc))

      try:
        csv_content = decoded.decode(char_enc["encoding"])
        self.logger.info("raw csv content: {}".format(csv_content))
      except UnicodeDecodeError as e:
        raise CsvContentDecodeError

      csv_content = csv_content \
          .replace('親,種類,名前', 'parent,type,name') \
          .replace(',場所,', ',place,') \
          .replace(',撮影対象,', ',target,')
      self.logger.info("replaced csv content: {}".format(csv_content))

      with open(csv_file, mode='w', encoding="utf_8_sig") as f:
        f.write(csv_content)
      filename = csv_file

    self.logger.info("input csv filename: {}".format(filename))
    csv = CsvOptimizer(filename)
    csv.set_project_id(self.project_id)
    self.places, self.targets = csv.optimize()
    self.logger.info("loaded places from csv file: {}".format(self.places))
    self.logger.info("loaded targets from csv file: {}".format(self.targets))

    return self.places, self.targets
    
  # csv => dictを読み出す
  def convert(self, places=None, targets=None, rewrite=True):
    if not self.project_id:
      raise ValueError
    if not places:
      places = self.places
    if not targets:
      targets = self.targets

    self.logger.info("convert before places: {}".format(places))
    self.logger.info("convert before targets: {}".format(targets))
    converted_places = []
    for place in places:
      if not place["project_id"] == self.project_id:
        raise ValueError
      converted_places.append(
         self.make_place_obj(place["place_id"], place["parent_place_id"], place["hierarchy"], place["name"], place["photos_required_num"])
      )
    self.logger.info("convert after places: {}".format(converted_places))

    converted_targets = []
    for target in targets:
      if not target["project_id"] == self.project_id:
        raise ValueError
      converted_targets.append(
          self.make_target_obj(target["target_id"], target["parent_place_id"], target["name"])
      )
    self.logger.info("convert after taregts: {}".format(converted_targets))

    if rewrite:
      self.places = converted_places
      self.targets = converted_targets
    return converted_places, converted_targets

  def batch_create(self, places=None, targets=None):
    self.logger.info("batch create to : {}".format(places))
    self.logger.info("batch create to : {}".format(targets))

    #TODO: 何を持って成功と見なす？
    ret = self.place_controller.batch_create_places(places)
    self.logger.info("create retuls: {}".format(ret))

    ret = self.target_controller.batch_create_targets(targets)
    self.logger.info("create retuls: {}".format(ret))

    return ret

  def make_place_obj(self, new_id, parent_place_id, hierarchy, name, photos_required_num=0):
    now = datetime.now().isoformat()
    return {
        "project_id": self.project_id,
        "place_id": new_id,
        "parent_place_id": parent_place_id,
        "hierarchy": hierarchy,
        "name": str(name),
        "photos": {
          "required": photos_required_num,
          "results": {
            "before": 0,
            "after": 0,
          }
        },
        "created_at": now,
        "updated_at": now
    }
  
  def make_target_obj(self, new_id, parent_place_id, name):
    now = datetime.now().isoformat()
    return {
        'project_id': self.project_id,
        'target_id': new_id,
        'parent_place_id': parent_place_id,
        'name': name,
        'photos': {
          'adopt': {
            'before': None,
            'after': None
          },
          "before": [],
          "after": []
        },
        'created_at': now,
        'updated_at': now
    }
