package back

import (
	"errors"
	"sync"

	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
)

var sessionKey = string("MySecretKey")

type session struct {
	Hash    string
	Manager int32

	Rows []session
}

var managerSession map[string]manager

var managerSessionMutex = sync.RWMutex{}

func prepareQueriesSession() []string {
	var e error
	ebox := make([]string, 0)

	queries["Select#Session#All"], e = db.Prepare(`SELECT "Hash", "Manager" FROM "Session"`)
	if e != nil {
		ebox = append(ebox, "Select#Session#All - "+e.Error())
	}
	queries["Insert#Session#"], e = db.Prepare(`INSERT INTO "Session"("Hash", "Manager") VALUES($1, $2)`)
	if e != nil {
		ebox = append(ebox, "Insert#Session# - "+e.Error())
	}
	queries["Delete#Session#"], e = db.Prepare(`DELETE FROM "Session" WHERE "Hash"=$1`)
	if e != nil {
		ebox = append(ebox, "Delete#Session# - "+e.Error())
	}

	return ebox
}

func (s *session) Insert() error {
	stmt, ok := queries["Insert#Session#"]
	if !ok {
		return errors.New("Запрос Insert#Session# не найден")
	}

	_, e := stmt.Exec(s.Hash, s.Manager)
	if e != nil {
		return errors.New("Не удалось создать сессию")
	}

	m := manager{}
	m.ID = s.Manager

	e = m.SelectByID()
	if e != nil {
		return loadSession()
	}

	managerSessionMutex.Lock()

	managerSession[s.Hash] = manager{
		ID:         m.ID,
		Login:      m.Login,
		Password:   m.Password,
		FirstName:  m.FirstName,
		LastName:   m.LastName,
		Patronymic: m.Patronymic,
		Role:       m.Role,
	}

	managerSessionMutex.Unlock()

	return nil
}

func (s *session) Delete() error {
	stmt, ok := queries["Delete#Session#"]
	if !ok {
		logger.Println("Запрос Delete#Session# не найден")
		return errors.New("Запрос Delete#Session# не найден")
	}

	_, e := stmt.Exec(s.Hash)
	if e != nil {
		logger.Println("Не удалось удалить сессию из базы данных")
		return errors.New("Не удалось удалить сессию из базы данных")
	}

	managerSessionMutex.Lock()

	delete(managerSession, s.Hash)

	managerSessionMutex.Unlock()

	return nil
}

func loadSession() error {
	stmt, ok := queries["Select#Session#All"]
	if !ok {
		return errors.New("Запрос Select#Session#All не найден")
	}

	rows, e := stmt.Query()
	if e != nil {
		return e
	}

	defer rows.Close()

	s := session{}
	m := manager{}

	managerSessionMutex.Lock()

	managerSession = make(map[string]manager)

	defer managerSessionMutex.Unlock()

	for rows.Next() {
		e = rows.Scan(&s.Hash, &m.ID)
		if e != nil {
			return e
		}

		e = m.SelectByID()
		if e != nil {
			logger.Println("Ошибка получения данных о менеджере с ID", m.ID, e)
			continue
		}

		managerSession[s.Hash] = manager{
			ID:         m.ID,
			Login:      m.Login,
			Password:   m.Password,
			FirstName:  m.FirstName,
			LastName:   m.LastName,
			Patronymic: m.Patronymic,
			Role:       m.Role,
		}
	}

	return nil
}

func getRoleFromContext(c *gin.Context) string {
	defer catch()

	cookieSession := sessions.Default(c)

	sessionHash, ok := cookieSession.Get(sessionKey).(string)
	if ok {
		managerSessionMutex.Lock()

		sessionManager, ok := managerSession[sessionHash]

		managerSessionMutex.Unlock()

		if ok {
			return sessionManager.Role
		}
	}

	return ""
}

func getManagerFromContext(c *gin.Context) *manager {
	defer catch()

	cookieSession := sessions.Default(c)

	sessionHash, ok := cookieSession.Get(sessionKey).(string)
	if ok {
		managerSessionMutex.Lock()

		sessionManager, ok := managerSession[sessionHash]

		managerSessionMutex.Unlock()

		if ok {
			return &sessionManager
		}
	}

	return nil
}

func clearSessionFromContext(c *gin.Context) {
	defer catch()

	cookieSession := sessions.Default(c)

	sessionHash, ok := cookieSession.Get(sessionKey).(string)
	if ok {
		s := session{}
		s.Hash = sessionHash
		s.Delete()

		cookieSession.Clear()
		cookieSession.Save()
		c.SetCookie("session", "", -1, "/", Setting.ServerDomain, false, true)
	}
}

func catch() {
	exception := recover()
	if exception != nil {
		logger.Println(exception)
	}
}
