import os
import sqlite3
from contextlib import closing
from flask import(
  g, current_app
)
from . import zm_client as zm

m_instance_path = ""

def init(app):
  app.before_request(userDb)
  app.teardown_appcontext(closeUserDb)
  global m_instance_path
  m_instance_path = app.instance_path

def userDb():
  if ('db' not in g) and g.userName and g.userName != 'admin':
    try: 
      dbPath = current_app.instance_path + '/users/{}.db'.format(g.userName)
      isExist = os.path.exists(dbPath)
      g.db = sqlite3.connect(
        dbPath,
        detect_types=sqlite3.PARSE_DECLTYPES,
        check_same_thread = False
      )    
      g.db.row_factory = sqlite3.Row
      if not isExist:
        initUserDb(g.db)
    except Exception as err:
      print('userDb failed: %s' % str(err))

def initUserDb(db):
  try: 
    with closing(db.cursor()) as cr:
      cr.execute('PRAGMA journal_mode=wal')
    
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblState ( \
          id   INTEGER PRIMARY KEY AUTOINCREMENT, \
          kind TEXT NOT NULL CHECK (kind <> ''));"
      )    
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.UNDEFINED.value}', 'undefined');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.READY.value}', 'ready');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.START.value}', 'start');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.RUNNING.value}', 'running');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.PAUSE.value}', 'pause');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.STOP.value}', 'stop');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.COMPLETED.value}', 'completed');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.ERROR.value}', 'error');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.CANCEL.value}', 'cancel');")
      cr.execute(f"INSERT INTO tblState(id, kind) VALUES ('{zm.StateType.NOT_RESPONDING.value}', 'notResponding');")
      db.commit()
      
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblPipeline ( \
          id            INTEGER PRIMARY KEY AUTOINCREMENT, \
          isDelete      INT NOT NULL DEFAULT 0 CHECK (isDelete BETWEEN 0 AND 1), \
          name          TEXT NOT NULL, \
          description   TEXT NOT NULL, \
          setts TEXT NOT NULL);"
      )
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblTaskTemplate ( \
          id INTEGER PRIMARY KEY AUTOINCREMENT, \
          averDurationSec INT NOT NULL CHECK (averDurationSec > 0), \
          maxDurationSec  INT NOT NULL CHECK (maxDurationSec > 0), \
          isDelete        INT NOT NULL DEFAULT 0 CHECK (isDelete BETWEEN 0 AND 1),  \
          script          TEXT NOT NULL CHECK (script <> ''), \
          name            TEXT NOT NULL CHECK (name <> ''), \
          description     TEXT NOT NULL);"
      )
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblPipelineTask( \
          id INTEGER PRIMARY KEY AUTOINCREMENT, \
          pplId         INT NOT NULL REFERENCES tblPipeline, \
          ttId          INT NOT NULL REFERENCES tblTaskTemplate, \
          isEnabled     INT NOT NULL DEFAULT 1 CHECK (isEnabled BETWEEN 0 AND 1), \
          isDelete      INT NOT NULL DEFAULT 0 CHECK (isDelete BETWEEN 0 AND 1), \
          hasChange     INT NOT NULL DEFAULT 0, \
          params        TEXT NOT NULL, \
          nextTasksId   TEXT NOT NULL, \
          prevTasksId   TEXT NOT NULL, \
          isStartNext   TEXT NOT NULL, \
          isSendResultToNext TEXT NOT NULL, \
          conditionStartNext TEXT NOT NULL, \
          name          TEXT NOT NULL CHECK (name <> ''), \
          description   TEXT NOT NULL, \
          setts TEXT NOT NULL);"   
      )
      cr.execute(
        "CREATE INDEX inxPipelineTaskPplId ON tblPipelineTask(PplId);"
      )
    
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblEvent( \
          id INTEGER PRIMARY KEY AUTOINCREMENT, \
          isEnabled     INT NOT NULL DEFAULT 1 CHECK (isEnabled BETWEEN 0 AND 1), \
          isDelete      INT NOT NULL DEFAULT 0 CHECK (isDelete BETWEEN 0 AND 1), \
          timeStartEverySec      INT NOT NULL, \
          timeLastStartEverySec  REAL NOT NULL DEFAULT 0, \
          timeStartOnceOfDay     TEXT NOT NULL, \
          timeLastStartOnceOfDay TEXT NOT NULL DEFAULT '', \
          tasksForStart TEXT NOT NULL, \
          name          TEXT NOT NULL CHECK (name <> ''), \
          description   TEXT NOT NULL);"
      )
      cr.execute(
        "CREATE TABLE IF NOT EXISTS tblTask( \
          id INTEGER PRIMARY KEY AUTOINCREMENT, \
          pplTaskId       INT NOT NULL REFERENCES tblPipelineTask, \
          starterPplTaskId INT REFERENCES tblPipelineTask, \
          starterEventId   INT REFERENCES tblEvent, \
          ttlId           INT NOT NULL REFERENCES tblTaskTemplate, \
          state           INT NOT NULL REFERENCES tblState,  \
          progress        INT NOT NULL DEFAULT 0,  \
          script          TEXT NOT NULL, \
          params          TEXT NOT NULL, \
          result          TEXT NOT NULL DEFAULT '', \
          createTime      TEXT NOT NULL DEFAULT '', \
          takeInWorkTime  TEXT NOT NULL DEFAULT '', \
          startTime       TEXT NOT NULL DEFAULT '', \
          stopTime        TEXT NOT NULL DEFAULT '');"
      )
      cr.execute(
        "CREATE INDEX inxTaskPplTaskId ON tblTask(pplTaskId);"
      )    
  except Exception as err:
    print('InitUserDb failed: %s' % str(err))
    
def closeUserDb(e = None):
  db = g.pop('db', None)
  if db is not None:
    db.close()

def createDb(userName : str):
  return createDbWithPath(m_instance_path, userName)
  
def createDbWithPath(path : str, userName : str):
  dbPath = path + '/users/{}.db'.format(userName)
  if os.path.exists(dbPath):
    db = sqlite3.connect(
      dbPath,
      detect_types=sqlite3.PARSE_DECLTYPES,
      check_same_thread = False
    )    
    db.row_factory = sqlite3.Row
    return db
  return None

def closeDb(db):
  if db is not None:
    db.close()