package models

import (
	"fmt"
	"git.dynamicdiscord.de/kalipso/zineshop/utils"
	"gorm.io/gorm"
	"math"
	"os/exec"
	"strings"
)

type PrintOption string

const (
	CoverPage     PrintOption = "-o FrontCoverPage=Printed -o FrontCoverTray=BypassTray"
	Colored       PrintOption = "-o SelectColor=Color"
	Grayscale     PrintOption = "-o SelectColor=Grayscale"
	LongEdge      PrintOption = ""
	ShortEdge     PrintOption = "-o Binding=TopBinding"
	CreateBooklet PrintOption = "-o Combination=Booklet -o PageSize=A5"
	TriFold       PrintOption = "-o Fold=TriFold -o Binding=TopBinding"
	FitToPage     PrintOption = "-o fit-to-page"
)

type OldPrintJob struct {
	Pdf     string
	Amount  uint
	Options []PrintOption
}

type Invoice struct {
	gorm.Model
	PrintJobs     []PrintJob
	PricePerClick float64
	PartCosts     float64
	PriceTotal    float64
}

type PrintJob struct {
	gorm.Model
	ShopItemID       uint
	ShopItem         ShopItem
	VariantID        uint
	Variant          ItemVariant
	PaperTypeId      uint
	PaperType        Paper `gorm:"foreignKey:PaperTypeId"`
	CoverPaperTypeId *uint
	CoverPaperType   *Paper `gorm:"foreignKey:CoverPaperTypeId"`
	Amount           uint
	PricePerPiece    float64
	PriceTotal       float64
	InvoiceID        uint
}

func GetPrintMode(mode string) PrintOption {
	if mode == "LongEdge" {
		return LongEdge
	}

	if mode == "ShortEdge" {
		return ShortEdge
	}

	if mode == "TriFold" {
		return TriFold
	}

	return CreateBooklet
}

func NewPrintJob(shopItem ShopItem, variant ItemVariant, paperType Paper, coverPaperType *Paper, amount uint) (PrintJob, error) {
	if shopItem.Pdf == "" {
		return PrintJob{}, fmt.Errorf("ShopItem has no PDF assigned")
	}

	if amount > 100 {
		return PrintJob{}, fmt.Errorf("Amount to big. This is denied for security reasons")
	}

	result := PrintJob{
		ShopItem:       shopItem,
		Variant:        variant,
		PaperType:      paperType,
		CoverPaperType: coverPaperType,
		Amount:         amount,
	}

	return result, nil
}

func (p *PrintJob) IsColored() bool {
	return p.Variant.Name == "Colored"
}

func (p *PrintJob) GeneratePrintOptions() []PrintOption {
	var result []PrintOption
	if p.Variant.Name == "Colored" {
		result = append(result, Colored)
	}

	if p.CoverPaperType != nil {
		result = append(result, CoverPage)
	}

	if p.ShopItem.FitToPage {
		result = append(result, FitToPage)
	}

	result = append(result, GetPrintMode(p.ShopItem.PrintMode))
	result = append(result, PrintOption(p.ShopItem.CustomPrintOptions))
	return result
}

func (p *PrintJob) Execute() error {
	baseCommand := "lp -d KonicaBooklet"
	baseCommand += fmt.Sprintf(" -n %v ", p.Amount)

	for _, option := range p.GeneratePrintOptions() {
		baseCommand += fmt.Sprintf(" %v ", option)
	}

	baseCommand += fmt.Sprintf(" -- %s", p.ShopItem.Pdf)

	parts := strings.Fields(baseCommand)

	// The first part is the command, the rest are the arguments
	fmt.Println(parts)
	cmd := exec.Command(parts[0], parts[1:]...)

	output, err := cmd.CombinedOutput()
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		return err
	}

	fmt.Printf("Output:\n%s\n", output)
	return nil
}

func (p *PrintJob) CalculatePrintCosts() (float64, error) {
	pageCount := utils.CountPagesAtPath(p.ShopItem.Pdf)

	if pageCount == 0 {
		fmt.Println("Pagecount of 0 - something is wrong here.")
		return 0, fmt.Errorf("Cant calculate price, pdf seems to be empty")
	}

	printMode := GetPrintMode(p.ShopItem.PrintMode)

	//Get actual pagecount depending on printmode
	actualPageCount := pageCount
	fmt.Println("PagCount: ", actualPageCount)

	if printMode == CreateBooklet {
		dividedCount := float64(pageCount) / 4.0
		actualPageCount = int(math.Ceil(dividedCount))
	}

	if printMode == LongEdge || printMode == ShortEdge {
		dividedCount := float64(pageCount) / 2.0
		actualPageCount = int(math.Ceil(dividedCount))
	}

	PPC := 0.002604
	partCost := 0.0067
	if p.IsColored() {
		partCost = 0.0478
	}

	printingCosts := float64(actualPageCount-1) * p.PaperType.Price

	if p.CoverPaperType != nil {
		printingCosts += p.CoverPaperType.Price
	} else {
		printingCosts += p.PaperType.Price
	}

	printingCosts += float64(actualPageCount/2) * PPC
	printingCosts += partCost * float64(actualPageCount)

	fmt.Printf("Printing Costs per Zine: %v\n", printingCosts)
	fmt.Printf("Printing Costs Total: %v\n", printingCosts*float64(p.Amount))
	p.PricePerPiece = printingCosts
	p.PriceTotal = printingCosts * float64(p.Amount)
	return printingCosts, nil
}
