package controllers

import (
	"fmt"
	"math"
	"strconv"
	"time"
	"zatobay_api/database"
	"zatobay_api/models"

	"github.com/gofiber/fiber/v2"
	"gorm.io/gorm"

	datatable "zatobay_api/utils/datatable"
)

type FilterAjaxTicket struct {
	Name    string `json:"ticket_name" url:"ticket_name" query:"ticket_name"`
	Price   string `json:"price" url:"price" query:"price"`
	Page    string `json:"page" url:"page" query:"page"`
	PerPage string `json:"length" url:"length" query:"length"`
	Sort    string `json:"column" url:"column" query:"column"`
	SortDir string `json:"dir" url:"dir" query:"dir"`
	Draw    string `json:"draw" url:"draw" query:"draw"`
}

func CreateTicket(c *fiber.Ctx) error {

	// user auth

	var data map[string]string
	var is_bulk byte

	if err := c.BodyParser(&data); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	if data["category"] == "UMUM" {
		is_bulk = 0
	} else {
		is_bulk = 1
	}

	ticket := models.Tickets{
		TicketName: data["ticket_name"],
		Price:      data["price"],
		Category:   data["category"],
		PriceShown: data["price_shown"],
		IsBulk:     is_bulk,
		CreatedAt:  time.Now(),
		UpdatedAt:  time.Now(),
	}

	res := database.DB.Create(&ticket)
	if res.Error != nil {
		return c.Status(400).JSON(fiber.Map{
			"message": "error",
			"error":   res.Error,
		})
	}
	return c.Status(200).JSON(fiber.Map{
		"message": "success",
		"data":    ticket,
	})
}

func GetTicket(c *fiber.Ctx) error {

	var ticket []models.Tickets

	result := database.DB.Find(&ticket)

	if result.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	return c.Status(200).JSON(fiber.Map{
		"message": "success",
		"ticket":  ticket,
	})
}

func GetTicketById(c *fiber.Ctx) error {

	var ticket models.Tickets

	id := c.Params("id")
	if id == "" {
		return c.JSON(fiber.Map{
			"message": "id must be set",
		})
	}

	id_int, err := strconv.Atoi(id)

	if err != nil {
		return err
	}

	result := database.DB.Find(&ticket, id_int)

	if result.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	return c.Status(200).JSON(fiber.Map{
		"message": "success",
		"data":    ticket,
	})
}

func GetTicketByCategory(c *fiber.Ctx) error {

	var ticket_umum []models.Tickets
	var ticket_sekolah []models.Tickets

	result_ticket_umum := database.DB.Where("category = ?", "UMUM").Order("id").Find(&ticket_umum)
	result_ticket_sekolah := database.DB.Where("category = ?", "SEKOLAH").Order("id").Find(&ticket_sekolah)

	if result_ticket_umum.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	if result_ticket_sekolah.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	res_umum := (fiber.Map{
		"data": ticket_umum,
	})

	res_sekolah := (fiber.Map{
		"data": ticket_sekolah,
	})

	return c.Status(200).JSON(fiber.Map{
		"message": "success",
		"umum":    res_umum,
		"sekolah": res_sekolah,
	})
}

func UpdateTicket(c *fiber.Ctx) error {
	var data map[string]string

	if err := c.BodyParser(&data); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	id := c.Params("id")
	if id == "" {
		return c.JSON(fiber.Map{
			"message": "id must be set",
		})
	}

	id_int, err := strconv.Atoi(id)

	if err != nil {
		return err
	}

	ticket := models.Tickets{
		TicketName: data["ticket_name"],
		Price:      data["price"],
		Category:   data["category"],
		PriceShown: data["price_shown"],
		UpdatedAt:  time.Now(),
	}

	var updatePriceShown map[string]interface{}

	updatePriceShown = map[string]interface{}{
		"price_shown": nil,
	}

	result := database.DB.Where("id = ?", id_int).Updates(&ticket)

	if data["category"] == "UMUM" {
		updt_price_shown := database.DB.Table("tickets").Where("id = ?", id_int).Updates(&updatePriceShown)
		if updt_price_shown.RowsAffected == 0 {
			return c.SendStatus(404)
		}
	}

	if result.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	return c.Status(200).JSON(fiber.Map{
		"message": "success update ticket, id :" + id,
		"data":    ticket,
	})
}

func ScanTicket(c *fiber.Ctx) error {
	var data map[string]string
	var check_ticket models.OrdersTicket
	var res_ticket models.OrdersTicket
	var order_details models.Orders
	var zero_time time.Time

	if err := c.BodyParser(&data); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	ticket := models.OrdersTicket{
		ScannedTime: time.Now(),
	}

	// check_id
	ticket_id_check := database.DB.Where("id = ?", data["id"]).First(&check_ticket)
	if ticket_id_check.RowsAffected == 0 {
		return c.Status(404).JSON(fiber.Map{
			"message": "Tiket Tidak ditemukan, id:" + data["id"],
			"status":  "not found",
		})
	}

	result := database.DB.Where("id = ? AND  Scanned_time = ?", data["id"], zero_time).Find(&check_ticket)
	database.DB.Where("id = ?", check_ticket.IdOrders).Find(&order_details)
	database.DB.Where("id = ?", data["id"]).Find(&res_ticket)
	if result.RowsAffected == 0 {
		return c.Status(200).JSON(fiber.Map{
			"message":       "Tiket telah terscan",
			"status":        "scanned",
			"data":          res_ticket,
			"order_details": order_details,
		})
	}

	update_status := database.DB.Where("id = ?", data["id"]).Updates(&ticket)
	database.DB.Where("id = ?", data["id"]).Find(&res_ticket)
	if update_status.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	// preparing struct for updating bulk order
	update_ticket_bulk := models.Orders{
		Scanned_count: order_details.QtyTicket,
		Updated_at:    time.Now(),
	}

	update_ticket_single := models.Orders{
		Id: check_ticket.IdOrders,
	}

	// update scanned_count
	if order_details.IsBulk == 1 {
		database.DB.Where("id = ?", check_ticket.IdOrders).Updates(&update_ticket_bulk)
	} else {
		database.DB.Model(&update_ticket_single).UpdateColumn("scanned_count", gorm.Expr("scanned_count + ?", 1))
		database.DB.Model(&update_ticket_single).UpdateColumn("updated_at", gorm.Expr("?", time.Now()))
	}
	fmt.Println(order_details)

	return c.Status(200).JSON(fiber.Map{
		"message":       "success update status ticket :" + data["id"],
		"status":        "new",
		"data":          res_ticket,
		"order_details": order_details,
	})
}

func DeleteTicket(c *fiber.Ctx) error {

	id := c.Params("id")
	if id == "" {
		return c.JSON(fiber.Map{
			"message": "id must be set",
		})
	}
	id_int, err := strconv.Atoi(id)

	if err != nil {
		return err
	}

	var ticket models.Tickets

	result := database.DB.Delete(&ticket, id_int)

	if result.RowsAffected == 0 {
		return c.SendStatus(404)
	}

	return c.Status(200).JSON(fiber.Map{
		"message": "success delete user, id :" + id,
	})
}

func TicketAjaxList(c *fiber.Ctx) error {

	filterAjax := new(FilterAjaxTicket)

	if err := c.QueryParser(filterAjax); err != nil {
		return c.Status(400).JSON(err.Error())
	}

	page, err := strconv.Atoi(filterAjax.Page)
	if err != nil {
		page = 1
	}
	perPage, err := strconv.Atoi(filterAjax.PerPage)
	if err != nil {
		perPage = 10
	}

	ticket := models.Tickets{
		TicketName: filterAjax.Name,
		Price:      filterAjax.Price,
	}

	var sort map[string]string
	sort = make(map[string]string)
	if filterAjax.Sort != "" {
		if filterAjax.SortDir == "" {
			sort[filterAjax.Sort] = "desc"
		} else {
			sort[filterAjax.Sort] = filterAjax.SortDir
		}
	}
	limit := perPage
	offset := (page - 1) * limit

	data, total, err := TicketQueryAjaxList(ticket, limit, offset, sort)

	if err != nil {
		return c.Status(400).JSON(err.Error())
	}
	pages := math.Ceil(float64(*total) / float64(perPage))
	meta := datatable.MetaDataTable{
		CurrentPage: page,
		From:        offset + 1,
		LastPage:    int(pages),
		Path:        "",
		PerPage:     fmt.Sprintf("%v", limit),
		To:          offset + limit,
		Total:       int(*total),
	}

	datatableFormat := datatable.DataTable{
		Meta: datatable.MetaDataTable{
			CurrentPage: page,
			From:        0,
			LastPage:    1,
			Path:        *datatable.BuildUrlForLink(c.Request(), 0),
			PerPage:     strconv.Itoa(perPage),
			To:          0,
			Total:       0,
		},
		Data: nil,
		Links: datatable.LinkDatatable{
			First: *datatable.BuildUrlForLink(c.Request(), 1),
			Last:  nil,
			Next:  nil,
			Prev:  nil,
		},
		Payload: filterAjax,
	}

	if err == nil {
		var dataResponseTicket []models.Tickets

		for _, data := range data {
			dataResponseTicket = append(dataResponseTicket, data)
		}

		datatableFormat.Data = dataResponseTicket
		meta.Path = *datatable.BuildUrlForLink(c.Request(), 0)
		datatableFormat.Meta = meta
		datatableFormat.Links = datatable.BuildLinksData(c.Request(), meta)
	}

	return c.JSON(fiber.Map{
		"message": "success",
		"data":    datatableFormat,
	})
}

func TicketQueryAjaxList(filter models.Tickets, limit, offset int, sort map[string]string) ([]models.Tickets, *int64, error) {
	var (
		result []models.Tickets
		count  int64
	)

	query := database.DB.Where(filter)
	if sort == nil {
		query = query.Order("id")
	} else {
		for key, val := range sort {
			query = query.Order(fmt.Sprintf("%s %s", key, val))
		}
	}
	query = query.Offset(offset).Limit(limit).Find(&result)
	fmt.Println(query)
	if query.Error != nil {
		return nil, nil, query.Error
	}
	dbCount := database.DB.Table("tickets").Where(filter).Select("id").Count(&count)
	if dbCount.Error != nil {
		return nil, nil, dbCount.Error
	}
	return result, &count, nil

}
