package back

import (
	"database/sql"
	"errors"
	"fmt"
	"strconv"
	"time"

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

var router *gin.Engine

var month map[string]string

func init() {

	initSetting()
	initLog()
	initPostgreSQL()

	m := manager{}
	fmt.Println("ADMIN EXISTS:", m.SelectTest())

	month = make(map[string]string)
	month["01"] = " января "
	month["02"] = " февраля "
	month["03"] = " марта "
	month["04"] = " апреля "
	month["05"] = " мая "
	month["06"] = " июня "
	month["07"] = " июля "
	month["08"] = " августа "
	month["09"] = " сентября "
	month["10"] = " октября "
	month["11"] = " ноября "
	month["12"] = " декабря "

	prepareMenu()
	prepareNews()
	prepareNewsPagination()
	prepareNotice()

	loadSession()

	router = gin.Default()

	word := sessions.NewCookieStore([]byte("RepairToSecretKey"))
	router.Use(sessions.Sessions("session", word))

	router.Static("/css/"+Setting.ServerVersion, Setting.Assets+"css/")
	router.Static("/js/"+Setting.ServerVersion, Setting.Assets+"js/")
	router.Static("/fonts", Setting.Assets+"font/")
	router.Static("/img", Setting.Assets+"img/")

	router.Use(static.Serve("/", static.LocalFile(Setting.Data, false)))

	router.LoadHTMLFiles(
		Setting.HTML+"index.html",
		Setting.HTML+"404.html",
		Setting.HTML+"position.html",
		Setting.HTML+"BigBoss.html",
		Setting.HTML+"Zapis.html",
		Setting.HTML+"Mass.html",
		Setting.HTML+"PageZap.html",
	)

	router.GET("/", handlerIndex)

	// "/данные/материал/hello world"
	// "/данные/новость/f9a8c99c874"
	// "/данные/объявление/89c304af9f7e"
	router.GET("/данные/:category/:link", handlerMaterial)
	router.GET("/страница/:page", handlerIndexPage)
	router.GET("/page/:page", handlerPages)

	router.POST("/order-select-time", handleGetTimeRecord)
	router.POST("/order-insert", handlerInsertOrder)
	router.POST("/order-select", handlerSelectOrder)

	router.POST("/get-materials", func(c *gin.Context) {
		infa := informationMass{}
		e := infa.Select()
		if e != nil {
			c.JSON(200, gin.H{
				"Error": e.Error(),
			})
			return
		}

		m := material{}
		var materials []material
		for _, link := range infa.Rows {
			m.Link = link
			e = m.SelectOne()
			if e != nil {
				continue
			}

			materials = append(materials, m)
		}

		c.JSON(200, gin.H{
			"Materials": materials,
		})
	})
	router.POST("/login", handlerLogin)
	router.POST("/logout", handlerLogout)

	router.POST("/set-material-list", handleSetMaterialList)
	// router.POST("/get-material-list", handleGetMaterialList)

	router.NoRoute(handlerNoRoute)

	prepareRouterMenu()
	prepareRouterMaterial()
	prepareRouterDocument()

	// buildTimeRecord()
}

// Run запустить сервер
func Run() {
	var e error

	if Setting.ServerSSL {
		e = router.RunTLS(Setting.ServerHost+":"+Setting.ServerPort, Setting.ServerSSLCert, Setting.ServerSSLKey)
	} else {
		e = router.Run(Setting.ServerHost + ":" + Setting.ServerPort)
	}

	logger.Println(e)
}

func handlerIndex(c *gin.Context) {

	isMain := true
	isAdmin := false
	isLogin := false

	isSecreter := false

	role := getRoleFromContext(c)
	if len(role) != 0 {
		if role == "__admin" {
			isAdmin = true
		} else if role == "_secretary" {
			isSecreter = true
		}
		isLogin = true
	}

	h := gin.H{}

	h["IsMain"] = isMain
	h["isSecreter"] = isSecreter
	h["IsLogin"] = isLogin
	h["IsAdmin"] = isAdmin
	if isAdmin {
		h["Menu"] = templateMenuAll
		h["MenuTop"] = templateMenuAllTop
	} else {
		h["Menu"] = templateMenu
		h["MenuTop"] = templateMenuTop
	}
	h["News"] = templateNews

	h["Pagination"] = templateNewsPagination
	h["Notice"] = templateNotice
	h["ServerVersion"] = Setting.ServerVersion

	c.HTML(200, "index.html", h)
}

func handlerPages(c *gin.Context) {

	isMain := false
	isAdmin := false
	isLogin := false

	isSecreter := false

	role := getRoleFromContext(c)
	if len(role) != 0 {
		if role == "__admin" {
			isAdmin = true
		} else if role == "_secretary" {
			isSecreter = true
		}
		isLogin = true
	}

	h := gin.H{}

	h["IsMain"] = isMain
	h["IsLogin"] = isLogin
	h["IsAdmin"] = isAdmin
	h["isSecreter"] = isSecreter
	// h["News"] = templateNews
	// h["Pagination"] = templateNewsPagination
	// h["Notice"] = templateNotice
	h["ServerVersion"] = Setting.ServerVersion
	h["AvailableMaterial"] = false
	h["Page"] = c.Param("page")

	c.HTML(200, "index.html", h)
}

func handlerIndexPage(c *gin.Context) {

	isMain := true
	isAdmin := false
	isLogin := false
	isSecreter := false

	role := getRoleFromContext(c)
	if len(role) != 0 {
		if role == "__admin" {
			isAdmin = true
		} else if role == "_secretary" {
			isSecreter = true
		}
		isLogin = true
	}

	page, e := strconv.ParseInt(c.Param("page"), 10, 64)
	if e != nil {
		logger.Println(e)
		page = 0
	}

	h := gin.H{}

	h["IsMain"] = isMain
	h["isSecreter"] = isSecreter
	h["IsLogin"] = isLogin
	h["IsAdmin"] = isAdmin
	if isAdmin {
		h["Menu"] = templateMenuAll
		h["MenuTop"] = templateMenuAllTop
	} else {
		h["Menu"] = templateMenu
		h["MenuTop"] = templateMenuTop
	}
	h["News"] = getNewsByPage(int(page))
	h["Pagination"] = getNewsPaginationByPage(int(page))
	h["Notice"] = templateNotice
	h["ServerVersion"] = Setting.ServerVersion

	c.HTML(200, "index.html", h)
}

func handlerLogin(c *gin.Context) {
	var m manager

	e := c.BindJSON(&m)
	if e != nil {
		fmt.Println(e.Error())

		c.JSON(200, gin.H{
			"Error": "Неверный логин/пароль",
		})
		return
	}

	e = m.Select()
	if e != nil {
		fmt.Println(e.Error())

		c.JSON(200, gin.H{
			"Error": "Неверный логин/пароль",
		})
		return
	}

	s := sessions.Default(c)

	hash, e := GenerateHash(m.Login)
	if e != nil {
		logger.Println(e)
		c.JSON(200, gin.H{
			"Error": "Ошибка сервера",
		})
		return
	}

	newSession := session{}
	newSession.Hash = hash
	newSession.Manager = m.ID

	e = newSession.Insert()
	if e != nil {
		logger.Println(e)
		c.JSON(200, gin.H{
			"Error": "Ошибка сервера",
		})
		return
	}

	s.Set(sessionKey, hash)

	e = s.Save()
	if e != nil {
		fmt.Println(e.Error())
		c.JSON(200, gin.H{
			"Error": "Ошибка сохранения сессии в куки",
		})
		return
	}

	c.JSON(200, gin.H{
		"Error": nil,
		"Name":  m.FirstName,
	})
}

func handlerLogout(c *gin.Context) {

	clearSessionFromContext(c)

	c.JSON(200, gin.H{
		"Error": nil,
	})
}

func handlerNoRoute(c *gin.Context) {
	h := gin.H{}

	h["Page"] = "400"

	isAdmin := false
	isLogin := false

	isSecreter := false

	role := getRoleFromContext(c)
	if len(role) != 0 {
		if role == "__admin" {
			isAdmin = true
		} else if role == "_secretary" {
			isSecreter = true
		}
		isLogin = true
	}

	h["IsMain"] = false
	h["isSecreter"] = isSecreter
	h["IsLogin"] = isLogin
	h["IsAdmin"] = isAdmin
	if isAdmin {
		h["Menu"] = templateMenuAll
		h["MenuTop"] = templateMenuAllTop
	} else {
		h["Menu"] = templateMenu
		h["MenuTop"] = templateMenuTop
	}
	h["AvailableMaterial"] = false

	h["ServerVersion"] = Setting.ServerVersion

	c.HTML(200, "index.html", h)
}

func handlerCatalog(c *gin.Context) {
	isMain := false
	isAdmin := false
	isLogin := false

	role := getRoleFromContext(c)
	if len(role) != 0 {
		if role == "__admin" {
			isAdmin = true
		}
		isLogin = true
	}

	h := gin.H{}

	h["IsMain"] = isMain
	h["IsLogin"] = isLogin
	h["IsAdmin"] = isAdmin

	h["Page"] = "catalog"
	h["AvailableMaterial"] = false
	h["ServerVersion"] = Setting.ServerVersion

	c.HTML(200, "index.html", h)
}

func handleSetMaterialList(c *gin.Context) {
	man := getManagerFromContext(c)

	if man.Role != "__admin" {
		c.JSON(200, gin.H{
			"Error": errorAccessRestricted,
		})
		return
	}

	m := informationMass{}

	e := c.BindJSON(&m)
	if e != nil {
		c.JSON(200, gin.H{
			"Error": e.Error(),
		})
		return
	}

	e = m.Insert()
	if e != nil {
		c.JSON(200, gin.H{
			"Error": "Материал с указанной ссылкой не существует",
		})
		return
	}

	c.JSON(200, gin.H{
		"Error": nil,
	})
}

// func handleGetMaterialList(c *gin.Context) {
// 	man := getManagerFromContext(c)

// 	if man.Role != "__admin" {
// 		c.JSON(200, gin.H{
// 			"Error": errorAccessRestricted,
// 		})
// 		return
// 	}

// 	m := informationMass{}

// 	e := m.Select()
// 	if e != nil {
// 		c.JSON(200, gin.H{
// 			"Error": e.Error(),
// 		})
// 		return
// 	}

// 	c.JSON(200, gin.H{
// 		"Error": nil,
// 		"Link":  m.Rows,
// 	})
// }

type timerecord struct {
	Date   string
	Time   int64
	Record sql.NullInt64

	Times []int64
}

func (m *timerecord) Insert() error {
	stmt, ok := queries["Insert@TimeRecord@"]
	if !ok {
		return errors.New("Insert@TimeRecord@ not found")
	}

	_, e := stmt.Exec(m.Date, m.Time, m.Record)
	if e != nil {
		return e
	}

	return nil
}

func (m *timerecord) Select() error {
	stmt, ok := queries["Select@TimeRecord@Times"]
	if !ok {
		return errors.New("ERROR: stmt[Select@TimeRecord@Times] IS NULL")
	}

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

	for rows.Next() {
		e = rows.Scan(&m.Time)
		if e != nil {
			return e
		}

		m.Times = append(m.Times, m.Time)
	}

	return nil
}

func (m *timerecord) Update() error {
	stmt, ok := queries["Update@TimeRecord@"]
	if !ok {
		return errors.New("ERROR: stmt[Update@TimeRecord@] IS NULL")
	}

	_, e := stmt.Exec(m.Record, m.Date, m.Time)
	if e != nil {
		return e
	}

	return nil
}

func handlerInsertOrder(c *gin.Context) {
	role := getRoleFromContext(c)
	if role != "__admin" {
		c.JSON(200, gin.H{
			"Error": "Отказано в доступе",
		})
	}

	n := timerecord{}
	m := record{}

	e := c.BindJSON(&m)
	if e != nil {
		fmt.Println(e)

		c.JSON(200, gin.H{
			"Error": e.Error(),
		})
		return
	}

	n.Date = m.Date
	n.Time = m.Time
	n.Record.Valid = true
	e = m.Insert()
	if e != nil {
		fmt.Println(e)

		c.JSON(200, gin.H{
			"Error": e.Error(),
		})
		return
	}
	n.Record.Int64 = m.ID

	e = n.Update()
	if e != nil {
		c.JSON(200, gin.H{
			"Error": "Не удалось создать запись. Возможно выбранное время уже кто-то успел занять несколькими секундами ранее :(",
		})

		return
	}

	c.JSON(200, gin.H{
		"Error": nil,
	})
}

func handlerSelectOrder(c *gin.Context) {

	m := record{}

	e := m.Select()
	if e != nil {
		fmt.Println(e)

		c.JSON(200, gin.H{
			"Error": e.Error(),
		})
		return
	}

	c.JSON(200, gin.H{
		"Error": nil,
		"Data":  m.Rows,
	})
}

func (m *record) Select() error {

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

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

	n := record{}
	for rows.Next() {

		//o."ID", o."Name", o."Passport", o."Phone", o."Cause", a."Date", a."Time"
		e = rows.Scan(
			&n.ID,
			&n.Name,
			&n.Passport,
			&n.Phone,
			&n.Cause,
			&n.Date,
			&n.Time,
		)
		if e != nil {
			return e
		}

		m.Rows = append(m.Rows, n)
	}

	return nil
}

type record struct {
	ID       int64  `json:"ID"`
	Name     string `json:"Name"`
	Passport string `json:"Passport"`
	Phone    string `json:"Phone"`
	Cause    string `json:"Cause"`

	Date string `json:"Date"`
	Time int64  `json:"Time"`

	Rows []record
}

func (m *record) Insert() error {
	stmt, ok := queries["Insert@Record@"]
	if !ok {
		return errors.New("ERROR: Insert@Record@ IS NULL")
	}

	row := stmt.QueryRow(m.Name, m.Passport, m.Phone, m.Cause)
	e := row.Scan(&m.ID)
	if e != nil {
		return e
	}

	return nil
}

func handleGetTimeRecord(c *gin.Context) {
	m := timerecord{}

	e := c.BindJSON(&m)
	if e != nil {
		c.JSON(200, gin.H{
			"Time": nil,
		})

		return
	}

	e = m.Select()
	if e != nil {
		c.JSON(200, gin.H{
			"Time": nil,
		})

		return
	}

	c.JSON(200, gin.H{
		"Time": m.Times,
	})
}

func buildTimeRecord() {

	m := timerecord{}

	d := time.Date(2020, time.June, 1, 0, 0, 0, 0, time.UTC)
	for i := 0; i < 90; i++ {

		if d.Weekday() == 0 || d.Weekday() == 6 {
			d = d.Add(8.64e+13)
			continue
		}

		var k int64
		for k = 0; k < 5; k++ {

			m.Date = d.String()[:10]
			m.Time = k
			m.Record.Valid = false

			m.Insert()
		}

		d = d.Add(8.64e+13)
	}
}
