package controllers

import (
	"encoding/csv"
	"errors"
	"fmt"
	"log"
	"math"
	"os"
	"reflect"
	"strconv"
	"time"
	"zatobay_api/database"
	"zatobay_api/models"
	datatable "zatobay_api/utils/datatable"

	"gorm.io/gorm"

	"github.com/gofiber/fiber/v2"
	strftime "github.com/itchyny/timefmt-go"
	"github.com/jung-kurt/gofpdf"
	"github.com/leekchan/accounting"
)

type FilterAjaxOrder struct {
	TicketName string `json:"ticket_name" url:"ticket_name" query:"ticket_name"`
	SchoolName string `json:"school_name" url:"school_name" query:"school_name"`
	StartDate  string `json:"start_date" url:"start_date" query:"start_date"`
	EndDate    string `json:"end_date" url:"end_date" query:"end_date"`
	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"`
}

type FilterDateRange struct {
	TicketName string `json:"ticket_name" url:"ticket_name" query:"ticket_name"`
	StartDate  string `json:"start_date" url:"start_date" query:"start_date"`
	EndDate    string `json:"end_date" url:"end_date" query:"end_date"`
}

func CreateOrder(c *fiber.Ctx) error {
	var data map[string]string
	var ticket models.Tickets
	var res_teacher models.Teacher
	var res_school models.Schools
	var nama_kasir string
	var is_bulk int
	var teacher_id string
	var school_id int

	current_time := time.Now()
	curr_time := strftime.Format(current_time, "%Y%m%d%H%M%S")

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

	qty, _ := strconv.Atoi(data["qty"])

	id_ticket_int, err_id_ticket := strconv.Atoi(data["id"])

	if err_id_ticket != nil {
		return err_id_ticket
	}

	// getting ticket status wether the ticket will printed every transaction or bulking
	is_bulk_status := data["is_bulk"]
	if is_bulk_status == "" {
		is_bulk = 0
	} else {
		is_bulk = 1
	}
	fmt.Println(is_bulk)

	// getting teacher_name

	teacher := data["teacher_id"]
	if teacher == "" {
		teacher_id = "0"
	} else {
		teacher_id = teacher
	}

	teacher_id_int, err := strconv.Atoi(teacher_id)
	if err != nil {
		return err
	}

	// get_nama_teacher
	database.DB.Find(&res_teacher, teacher_id_int)

	// checking school_name
	school_id_check := data["school_id"]

	if school_id_check == "" {
		school_id = res_teacher.SchoolId
	} else {
		school_int, err := strconv.Atoi(school_id_check)
		if err != nil {
			return err
		}
		school_id = school_int
	}

	// get school_name
	database.DB.Find(&res_school, school_id)

	get_ticket := database.DB.Find(&ticket, id_ticket_int)
	if get_ticket.Error != nil {
		return c.Status(400).JSON(fiber.Map{
			"status": "Error",
			"Error":  get_ticket.Error,
		})
	}

	r := reflect.ValueOf(&ticket)
	ticket_name := reflect.Indirect(r).FieldByName("TicketName")
	ticket_category := reflect.Indirect(r).FieldByName("Category")
	ticket_price := reflect.Indirect(r).FieldByName("Price")
	price_shown := reflect.Indirect(r).FieldByName("PriceShown")

	if c.Get("Authorization") == "" {
		return c.JSON(fiber.Map{
			"message": "Auth Token must be set",
		})
	}

	err, id_kasir, nama_kasir := GetNamaKasir(c.Get("Authorization"))
	id_kasir_int, err_id_kasir := strconv.Atoi(id_kasir)

	if err_id_kasir != nil {
		return c.Status(400).JSON(fiber.Map{
			"status": "cannot convert cashier id",
		})
	}

	if err != nil {
		return c.Status(400).JSON(fiber.Map{
			"status": "Error get cashier name",
		})
	}

	if err_id_kasir != nil {
		return err_id_kasir
	}

	order_id := ticket_category.String() + curr_time

	order := models.Orders{
		Id:            order_id,
		TicketName:    ticket_name.String(),
		QtyTicket:     qty,
		Price:         ticket_price.String(),
		Category:      ticket_category.String(),
		Trxdate:       time.Now(),
		UserId:        id_kasir_int,
		NamaKasir:     nama_kasir,
		Scanned_count: 0,
		IsBulk:        is_bulk,
		TeacherId:     teacher_id_int,
		TeacherName:   res_teacher.TeacherName,
		SchoolName:    res_school.SchoolName,
		PriceShown:    price_shown.String(),
		Created_at:    time.Now(),
		Updated_at:    time.Now(),
	}

	create_order := database.DB.Create(&order)
	if create_order.Error != nil {
		return c.Status(400).JSON(fiber.Map{
			"status": "Error",
			"Error":  create_order.Error,
		})
	}

	log.Println("Create Order Id :", order)

	var orderTicketArr []models.OrdersTicket

	// define how to print ticket if is bulk true, is only print one ticket, is false, printing depend quantity of ticket.
	if is_bulk == 1 {
		qty = 1
	}

	for i := 0; i < qty; i++ {

		order_ticket := models.OrdersTicket{
			Id:        curr_time + data["category"] + "-" + strconv.Itoa(i),
			IdOrders:  order_id,
			IsBulk:    is_bulk,
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		}

		orderTicketArr = append(orderTicketArr, order_ticket)

	}

	database.DB.Create(orderTicketArr)

	return c.Status(200).JSON(fiber.Map{
		"status":       "success",
		"order":        order,
		"order_ticket": orderTicketArr,
	})

}

func CreateTicketDetails(c *fiber.Ctx) error {

	var id string = "umum123"
	var ids string = "test_umum"
	var qty int = 5
	var orderTicketArr []models.OrdersTicket

	current_time := time.Now()
	curr_time := strftime.Format(current_time, "%Y%m%d%H%M%S")

	for i := 0; i < qty; i++ {

		order_ticket := models.OrdersTicket{
			Id:       curr_time + ids + "-" + strconv.Itoa(i),
			IdOrders: id,
		}

		orderTicketArr = append(orderTicketArr, order_ticket)

	}

	database.DB.Create(orderTicketArr)

	return c.JSON(fiber.Map{
		"status":       "success",
		"order_ticket": orderTicketArr,
	})
}

func GetOrderById(c *fiber.Ctx) error {

	order := models.Orders{
		Id: c.Params("id"),
	}
	var order_ticket []models.OrdersTicket

	result_order := database.DB.Find(&order)
	result_order_ticket := database.DB.Where("id_orders= ?", c.Params("id")).Find(&order_ticket)
	fmt.Println(order_ticket)
	fmt.Println(result_order_ticket.RowsAffected)

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

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

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

func OrderAjaxList(c *fiber.Ctx) error {

	filterAjax := new(FilterAjaxOrder)

	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
	}

	order := models.Orders{
		TicketName: filterAjax.TicketName,
		SchoolName: filterAjax.SchoolName,
	}

	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

	var start, end string

	if filterAjax.StartDate != "" && filterAjax.EndDate != "" {
		start = filterAjax.StartDate + " 00:00:00"
		end = filterAjax.EndDate + " 23:59:59"
	}

	fmt.Println(start)
	fmt.Println(end)

	data, total, err := OrderQueryAjaxList(order, limit, offset, sort, start, end)
	fmt.Println(data)

	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.Orders

		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)
	}

	type MetaInfo struct {
		TotalTicket int64 `json:"total_ticket"`
		TotalIncome int64 `json:"total_income"`
		TotalTrx    int64 `json:"total_trx"`
		TotalScan   int64 `json:"total_scan"`
	}

	var results MetaInfo

	database.DB.Table("orders").Select("sum(qty_ticket) as total_ticket, sum(qty_ticket*cast(price as int)) as total_income, count(*) as total_trx, sum(scanned_count) as total_scan").Where(order).Where("trxdate between ? and ?", start, end).Scan(&results)
	fmt.Print(results)

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

func OrderAjaxListCashierDaily(c *fiber.Ctx) error {

	filterAjax := new(FilterAjaxOrder)

	var start, end string

	t := time.Now()
	curr_time := strftime.Format(t, "%Y-%m-%d")
	start = curr_time + " 00:00:00"
	end = curr_time + " 23:59:59"

	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 = 20
	}

	order := models.Orders{
		TicketName: filterAjax.TicketName,
		SchoolName: filterAjax.SchoolName,
	}

	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 := OrderQueryAjaxListCashierDaily(order, limit, offset, sort, start, end)
	fmt.Println(data)

	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.Orders

		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)
	}

	type MetaInfo struct {
		TotalTicket int64 `json:"total_ticket"`
		TotalIncome int64 `json:"total_income"`
		TotalTrx    int64 `json:"total_trx"`
		TotalScan   int64 `json:"total_scan"`
	}

	var results MetaInfo

	database.DB.Table("orders").Select("sum(qty_ticket) as total_ticket, sum(qty_ticket*cast(price as int)) as total_income, count(*) as total_trx, sum(scanned_count) as total_scan").Where(order).Where("trxdate between ? and ?", start, end).Scan(&results)
	fmt.Print(results)

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

func OrderQueryAjaxList(filter models.Orders, limit, offset int, sort map[string]string, start string, end string) ([]models.Orders, *int64, error) {
	var (
		result         []models.Orders
		count          int64
		query, dbCount *gorm.DB
	)

	if start == "" {
		query = database.DB.Where(filter)
	} else {
		query = database.DB.Where(filter).Where("trxdate BETWEEN ? AND ?", start, end)
	}

	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
	}
	if start == "" {
		dbCount = database.DB.Table("orders").Where(filter).Select("id").Count(&count)
	} else {
		dbCount = database.DB.Table("orders").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Select("id").Count(&count)
	}

	if dbCount.Error != nil {
		return nil, nil, dbCount.Error
	}
	return result, &count, nil

}

func OrderQueryAjaxListCashierDaily(filter models.Orders, limit, offset int, sort map[string]string, start string, end string) ([]models.Orders, *int64, error) {
	var (
		result         []models.Orders
		count          int64
		query, dbCount *gorm.DB
	)

	if start == "" {
		query = database.DB.Where(filter)
	} else {
		query = database.DB.Where(filter).Where("trxdate BETWEEN ? AND ?", start, end)
	}

	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).Order("trxdate desc").Find(&result)
	fmt.Println(query)
	if query.Error != nil {
		return nil, nil, query.Error
	}
	if start == "" {
		dbCount = database.DB.Table("orders").Where(filter).Select("id").Count(&count)
	} else {
		dbCount = database.DB.Table("orders").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Select("id").Count(&count)
	}

	if dbCount.Error != nil {
		return nil, nil, dbCount.Error
	}
	return result, &count, nil

}

func DeleteOrder(c *fiber.Ctx) error {

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

	orders := models.Orders{
		Id: Id,
	}

	result := database.DB.Delete(&orders)

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

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

func OrderDaily(c *fiber.Ctx) error {
	// var order models.Orders
	// var res_daily_order []models.DailyOrder
	t := time.Now()
	curr_time := strftime.Format(t, "%Y-%m-%d")
	start_time := curr_time + " 00:00:00"
	end_time := curr_time + " 23:59:59"
	fmt.Println(start_time, end_time)

	// Select `id`, `name` automatically when querying
	// .Where("category IN ?", [][]interface{}{{"UMUM", "SEKOLAH"}})
	// order_daily := database.DB.Model(&order).Where("trxdate between ? and ?", start_time, end_time).Group("ticket_name").Find(&res_daily_order)

	type Result struct {
		TicketName string `json:"ticket_name"`
		Price      string `json:"price"`
		Category   string `json:"category"`
		Total      int64  `json:"total"`
		Total_Scan int64  `json:"total_scan"`
	}

	var results []Result

	database.DB.Table("orders").Select("ticket_name, price, category, sum(qty_ticket) as total, sum(scanned_count) as total_scan").Where("trxdate between ? and ?", start_time, end_time).Where("category IN ?", []string{"UMUM", "SEKOLAH"}).Group("ticket_name").Scan(&results)
	fmt.Print(results)

	return c.Status(200).JSON(fiber.Map{
		"data":   results,
		"status": "success",
		"time":   t,
	})
}

func ExportOrder(c *fiber.Ctx) error {
	filter := new(FilterDateRange)

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

	type MetaInfo struct {
		TotalTicket int64 `json:"total_ticket"`
		TotalIncome int64 `json:"total_income"`
		TotalTrx    int64 `json:"total_trx"`
		TotalScan   int64 `json:"total_scan"`
	}

	var info = new(MetaInfo)

	order := models.Orders{
		TicketName: filter.TicketName,
	}

	start := filter.StartDate + " 00:00:00"
	end := filter.EndDate + " 23:59:59"
	fmt.Println(start)
	data, totalTrx, totalTicket, totalIncome, totalScan, err := OrderQueryExport(order, start, end)
	if err != nil {
		return c.Status(400).JSON(err.Error())
	}

	info.TotalTrx = totalTrx
	info.TotalTicket = totalTicket
	info.TotalIncome = totalIncome
	info.TotalScan = totalScan

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

func OrderQueryExport(filter models.Orders, start string, end string) ([]models.Orders, int64, int64, int64, int64, error) {
	var (
		result                                               []models.Orders
		countTrx, countTicket, countIncome, countScan        int64
		query, totalTrx, totalTicket, totalIncome, totalScan *gorm.DB
	)

	query = database.DB.Where(filter).Where("trxdate BETWEEN ? AND ?", start, end)

	query = query.Find(&result)
	if query.Error != nil {
		return nil, 0, 0, 0, 0, query.Error
	}

	totalTrx = database.DB.Table("orders").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Count(&countTrx)

	if totalTrx.Error != nil {
		return nil, 0, 0, 0, 0, query.Error
	}

	totalTicket = database.DB.Table("orders").Select("sum(qty_ticket) as total_ticket").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Scan(&countTicket)

	if totalTicket.Error != nil {
		return nil, 0, 0, 0, 0, query.Error
	}

	totalIncome = database.DB.Table("orders").Select("sum(qty_ticket*cast(price as int)) as total_income").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Scan(&countIncome)

	if totalIncome.Error != nil {
		return nil, 0, 0, 0, 0, query.Error
	}

	totalScan = database.DB.Table("orders").Select("sum(scanned_count) as total_scan").Where(filter).Where("trxdate BETWEEN ? AND ?", start, end).Scan(&countScan)

	if totalScan.Error != nil {
		return nil, 0, 0, 0, 0, query.Error
	}

	return result, countTrx, countTicket, countIncome, countScan, nil

}

func PrintPdfOrder(c *fiber.Ctx) error {

	// filter := new(FilterDateRange)
	filter := new(FilterAjaxOrder)

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

	type MetaInfo struct {
		TotalTicket int64 `json:"total_ticket"`
		TotalIncome int64 `json:"total_income"`
		TotalTrx    int64 `json:"total_trx"`
		TotalScan   int64 `json:"total_scan"`
	}

	var info = new(MetaInfo)

	order := models.Orders{
		TicketName: filter.TicketName,
		SchoolName: filter.SchoolName,
	}

	start := filter.StartDate + " 00:00:00"
	end := filter.EndDate + " 23:59:59"
	fmt.Println(start)
	_, totalTrx, totalTicket, totalIncome, totalScan, err := OrderQueryExport(order, start, end)
	if err != nil {
		return c.Status(400).JSON(err.Error())
	}

	info.TotalTrx = totalTrx
	info.TotalTicket = totalTicket
	info.TotalIncome = totalIncome
	info.TotalScan = totalScan

	fmt.Println("TOTAL INCOME")
	fmt.Print(info.TotalIncome)

	// data group by ticket name

	type Result struct {
		TicketName string `json:"ticket_name"`
		Price      string `json:"price"`
		Category   string `json:"category"`
		Total      int64  `json:"total"`
		Total_Scan int64  `json:"total_scan"`
	}

	var results []Result

	database.DB.Table("orders").Select("ticket_name, price, category, sum(qty_ticket) as total, sum(scanned_count) as total_scan").Where("trxdate between ? and ?", start, end).Where(order).Group("ticket_name").Group("price").Scan(&results)
	// fmt.Print(results)

	// create csv file

	file, err := os.Create("./print/order.csv")
	defer file.Close()
	if err != nil {
		log.Fatalln("failed to open file", err)
	}
	w := csv.NewWriter(file)
	defer w.Flush()

	// define column headers
	headers := []string{
		"Ticket Name",
		"Price",
		"Category",
		"Ticket Terjual",
	}

	w.Write(headers)

	fmt.Println(headers)

	var data_csv [][]string
	for _, record := range results {
		row := []string{record.TicketName, record.Price, record.Category, strconv.Itoa(int(record.Total))}
		data_csv = append(data_csv, row)
	}

	w.WriteAll(data_csv)

	// // load csv

	if _, err := os.Stat(path()); errors.Is(err, os.ErrNotExist) {
		return c.SendStatus(404)
	}

	data_pdf := loadCSV(path())

	pdf := gofpdf.New("P", "mm", "A4", "")

	pdf.AddPage()

	pdf.SetFont("Helvetica", "", 12)
	pdf.WriteAligned(0, 38, fmt.Sprintf("Dari Tanggal : %s", filter.StartDate), "R")
	pdf.Ln(5)
	pdf.WriteAligned(0, 38, fmt.Sprintf("Sampai Tanggal : %s", filter.EndDate), "R")
	pdf.SetFont("Arial", "B", 24)
	pdf.WriteAligned(0, 35, "Zatobay Report", "C")

	pdf = image(pdf)
	ac := accounting.Accounting{Symbol: "Rp.", Precision: 2, Thousand: ".", Decimal: ","}

	pdf.Ln(10)
	pdf.SetFont("Helvetica", "", 12)

	pdf.WriteAligned(0, 38, fmt.Sprintf("Total Pendapatan : %s", ac.FormatMoney(totalIncome)), "L")
	pdf.Ln(5)
	pdf.WriteAligned(0, 38, fmt.Sprintf("Total Transaksi	: %d", totalTrx), "L")
	pdf.Ln(5)
	pdf.WriteAligned(0, 38, fmt.Sprintf("Total Tiket Terjual	: %d", totalTicket), "L")
	pdf.Ln(30)

	pdf = header(pdf, data_pdf[0])
	pdf = table(pdf, data_pdf[1:])

	if pdf.Err() {
		log.Fatalf("Failed creating PDF report: %s\n", pdf.Error())
	}

	pdf_path := "./print/order.pdf"

	err_pdf := pdf.OutputFileAndClose(pdf_path)
	if err_pdf != nil {
		log.Println("ERROR", err.Error())
	}

	if _, err := os.Stat(pdf_path); errors.Is(err, os.ErrNotExist) {
		return c.SendStatus(404)
	}

	return c.Download(pdf_path)

}

func path() string {
	if len(os.Args) < 2 {
		return "./print/order.csv"
	}
	return os.Args[1]
}

func loadCSV(path string) [][]string {
	f, err := os.Open(path)
	if err != nil {
		log.Fatalf("Cannot open '%s': %s\n", path, err.Error())
	}
	defer f.Close()
	r := csv.NewReader(f)
	rows, err := r.ReadAll()
	if err != nil {
		log.Fatalln("Cannot read CSV data:", err.Error())
	}
	return rows
}

func header(pdf *gofpdf.Fpdf, hdr []string) *gofpdf.Fpdf {
	pdf.SetFont("Times", "B", 14)
	pdf.SetFillColor(240, 240, 240)
	for _, str := range hdr {
		// The `CellFormat()` method takes a couple of parameters to format
		// the cell. We make use of this to create a visible border around
		// the cell, and to enable the background fill.
		pdf.CellFormat(45, 7, str, "1", 0, "", true, 0, "")
	}

	// Passing `-1` to `Ln()` uses the height of the last printed cell as
	// the line height.
	pdf.Ln(-1)
	return pdf
}

// ## The Table Body

// In the same fashion, we can create the table body.

func table(pdf *gofpdf.Fpdf, tbl [][]string) *gofpdf.Fpdf {
	// Reset font and fill color.
	pdf.SetFont("Times", "", 10)
	pdf.SetFillColor(255, 255, 255)

	// Every column gets aligned according to its contents.
	align := []string{"L", "C", "L", "L", "R", "R"}
	for _, line := range tbl {
		for i, str := range line {
			// Again, we need the `CellFormat()` method to create a visible
			// border around the cell. We also use the `alignStr` parameter
			// here to print the cell content either left-aligned or
			// right-aligned.
			pdf.CellFormat(45, 7, str, "1", 0, align[i], false, 0, "")
		}
		pdf.SetFooterFunc(func() {
			// Position at 1.5 cm from bottom
			pdf.SetY(-15)
			// Arial italic 8
			pdf.SetFont("Arial", "I", 8)
			// Text color in gray
			pdf.SetTextColor(128, 128, 128)
			// Page number
			pdf.CellFormat(0, 10, fmt.Sprintf("Page %d", pdf.PageNo()),
				"", 0, "C", false, 0, "")
		})
		pdf.Ln(-1)
	}
	return pdf
}

// ## The Image

// Next, let's not forget to impress our boss by adding a fancy image.
func image(pdf *gofpdf.Fpdf) *gofpdf.Fpdf {
	// The `ImageOptions` method takes a file path, x, y, width, and height
	// parameters, and an `ImageOptions` struct to specify a couple of options.
	pdf.ImageOptions("./img/zatobay-print.jpg", 10, 10, 70, 22, false, gofpdf.ImageOptions{ImageType: "JPG", ReadDpi: true}, 0, "")
	return pdf
}

// ## Saving The Document
//
// Finally, the convenience method `OutputFileAndClose()` lets us save the
// finished document.
func savePDF(pdf *gofpdf.Fpdf) error {
	return pdf.OutputFileAndClose("report.pdf")
}
