package qrbill

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"html/template"
	"strings"

	"git.schreifuchs.ch/lou-taylor/accounting/pkg/invoice/model"
	"github.com/skip2/go-qrcode"
)

// Invoice contains all necessary information for the generation of an invoice.
type Invoice struct {
	ReceiverIBAN    string `yaml:"receiver_iban" default:"CH44 3199 9123 0008 8901 2"`
	IsQrIBAN        bool   `yaml:"is_qr_iban" default:"true"`
	ReceiverName    string `yaml:"receiver_name" default:"Robert Schneider AG"`
	ReceiverStreet  string `yaml:"receiver_street" default:"Rue du Lac"`
	ReceiverNumber  string `yaml:"receiver_number" default:"12"`
	ReceiverZIPCode string `yaml:"receiver_zip_code" default:"2501"`
	ReceiverPlace   string `yaml:"receiver_place" default:"Biel"`
	ReceiverCountry string `yaml:"receiver_country" default:"CH"`
	PayeeName       string `yaml:"payee_name" default:"Pia-Maria Rutschmann-Schnyder"`
	PayeeStreet     string `yaml:"payee_street" default:"Grosse Marktgasse"`
	PayeeNumber     string `yaml:"payee_number" default:"28"`
	PayeeZIPCode    string `yaml:"payee_zip_code" default:"9400"`
	PayeePlace      string `yaml:"payee_place" default:"Rorschach"`
	PayeeCountry    string `yaml:"payee_country" default:"CH"`
	Reference       string `yaml:"reference" default:"21 00000 00003 13947 14300 09017"`
	AdditionalInfo  string `yaml:"additional_info" default:"Rechnung Nr. 3139 für Gartenarbeiten"`
	Amount          string `yaml:"amount" default:"3 949.75"`
	Currency        string `yaml:"currency" default:"CHF"`
}

func New(amount string, receiver model.Entity, payee *model.Entity) Invoice {
	i := Invoice{
		ReceiverIBAN:    receiver.IBAN,
		IsQrIBAN:        false,
		ReceiverName:    receiver.Name,
		ReceiverStreet:  receiver.Address.Street,
		ReceiverNumber:  receiver.Address.Number,
		ReceiverZIPCode: receiver.Address.ZIPCode,
		ReceiverPlace:   receiver.Address.Place,
		ReceiverCountry: receiver.Address.Country,
		Reference:       generateReference(),
		Amount:          amount,
		Currency:        "CHF",
	}
	if payee != nil {
		i.PayeeName = payee.Name
		i.PayeeStreet = payee.Address.Street
		i.PayeeNumber = payee.Address.Number
		i.PayeeZIPCode = payee.Address.ZIPCode
		i.PayeePlace = payee.Address.Place
		i.PayeeCountry = payee.Address.Country
	}
	return i
}

func (i Invoice) GetQR() (src template.URL, err error) {
	content, err := i.qrContent()
	if err != nil {
		return
	}
	qr, err := qrcode.New(content, qrcode.Medium)
	if err != nil {
		return
	}
	qr.DisableBorder = true
	png, err := qr.PNG(512)
	if err != nil {
		return
	}
	b64 := base64.StdEncoding.EncodeToString(png)
	src = template.URL(fmt.Sprintf(`data:image/png;base64,%s`, b64))
	return
}

// noPayee returns true if no fields of the payee are set
func (i Invoice) noPayee() bool {
	return i.PayeeName == "" && i.PayeeStreet == "" && i.PayeeZIPCode == "" && i.PayeePlace == ""
}

func (i Invoice) qrContent() (string, error) {
	qrTpl := `SPC\r
0200\r
1\r
{{ .iban }}\r
S\r
{{ .inv.ReceiverName }}\r
{{ .inv.ReceiverStreet }}\r
{{ .inv.ReceiverNumber }}\r
{{ .inv.ReceiverZIPCode }}\r
{{ .inv.ReceiverPlace }}\r
{{ .inv.ReceiverCountry }}\r
\r
\r
\r
\r
\r
\r
\r
{{ .amount }}\r
{{ .inv.Currency }}\r
{{ .payeeAdrType }}\r
{{ .inv.PayeeName }}\r
{{ .inv.PayeeStreet }}\r
{{ .inv.PayeeNumber }}\r
{{ .inv.PayeeZIPCode }}\r
{{ .inv.PayeePlace }}\r
{{ .inv.PayeeCountry }}\r
{{ .refType }}\r
{{ .reference }}\r
{{ .inv.AdditionalInfo }}\r
EPD
`
	refType := "QRR"
	if !i.IsQrIBAN {
		refType = "SCOR"
	}
	payeeAdrType := "S"
	if i.noPayee() {
		payeeAdrType = ""
	}
	data := map[string]any{
		"inv":          i,
		"iban":         strings.ReplaceAll(i.ReceiverIBAN, " ", ""),
		"amount":       strings.ReplaceAll(i.Amount, " ", ""),
		"payeeAdrType": payeeAdrType,
		"reference":    strings.ReplaceAll(i.Reference, " ", ""),
		"refType":      refType,
	}
	tpl, err := template.New("qr-content").Parse(qrTpl)
	if err != nil {
		return "", fmt.Errorf("error while creating qr template: %s", err)
	}
	var buf bytes.Buffer
	if err := tpl.Execute(&buf, data); err != nil {
		return "", fmt.Errorf("error while applying data to qr template: %s", err)
	}
	rsl := strings.ReplaceAll(buf.String(), "\\r", "\r")
	return rsl, nil
}
