// Set GST rates on the POS catalogue products.
//
// The four packaged lines at 1185 sit at taxpercent 0 and are being billed with
// no GST at all — a live compliance problem rather than a cosmetic one. Those
// are written.
//
// The produce at 1135 is NOT all zero, which is what this was first written
// believing. It holds 8, 12 and 18, and under Indian GST fresh unbranded fruit
// and chilled fish are nil-rated — so several look like overcharging. Every
// correction there is a *reduction* of a live rate, which is a decision for
// whoever signs the returns. Reported as REVIEW and left untouched.
//
//	go run ./scratch/gstrates plan
//	go run ./scratch/gstrates apply
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/joho/godotenv"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// Indian GST on food, as it applies to these lines.
//
// Fresh, unbranded and unpackaged produce is nil-rated, which is why the fruit
// stays at 0 rather than being "not set yet". Packaged branded snacks are 12%.
// Breakfast cereal is 18%.
//
// Fish is the one worth stating: fresh or chilled is nil-rated, and only
// frozen/branded/packaged attracts 5%. Left at 0 on the reading that a counter
// selling loose Mysore bananas is selling fresh fish, not frozen packs.
type rate struct {
	productID int
	name      string
	percent   float64
	why       string

	// apply gates the write. Only rows that are unambiguously *unset* are
	// written; anything already carrying a rate is reported and left alone.
	//
	// The fresh produce at 1135 is the reason for this flag. Those rows are not
	// blank — they hold 8, 12 and 18 — and under Indian GST fresh unbranded
	// fruit and chilled fish are nil-rated, so several look like overcharging.
	// But *lowering* a live tax rate is a compliance decision belonging to
	// whoever signs the returns, not a bug to be quietly corrected by a script,
	// and someone is actively working on pricing in this repo. Reported, not
	// touched.
	apply bool
}

var rates = []rate{
	// 1135 — fresh produce, nil-rated under Indian GST.
	//
	// These were held back at first because every one is a *reduction* of a
	// live rate, which is a compliance decision rather than a bug fix. Released
	// on the owner's explicit instruction after that was put to them.
	//
	// Two readings are assumed and should be checked against what the counter
	// actually sells: Maceral and Tuna are taken as fresh or chilled, which is
	// nil-rated — frozen, branded or packaged fish is 5%. Hatsun curd is taken
	// as plain curd, which is nil-rated — flavoured yoghurt is 5%.
	{6988, "Mysore Banana", 0, "fresh fruit — nil-rated, currently 8%", true},
	{6989, "Jammu Apple", 0, "fresh fruit — nil-rated, currently 18%", true},
	{6990, "Small orange", 0, "fresh fruit — nil-rated, currently 18%", true},
	{6991, "Red Guava", 0, "fresh fruit — nil-rated, currently 18%", true},
	{6992, "Pomegrante", 0, "fresh fruit — nil-rated, currently 12%", true},
	{6993, "Salem Mango", 0, "fresh fruit — nil-rated", true},
	{6994, "Pineapple", 0, "fresh fruit — nil-rated", true},
	{6995, "Strawberries", 0, "fresh fruit — nil-rated, currently 18%", true},
	{6996, "Maceral", 0, "fresh fish nil-rated; 5% only if frozen/packaged", true},
	{6997, "Tuna", 0, "fresh fish nil-rated; 5% only if frozen/packaged", true},
	{6998, "Hatsun curd", 0, "curd nil-rated; flavoured yoghurt would be 5%", true},
	{7014, "Apple", 0, "fresh fruit — nil-rated", true},

	// 1185 — genuinely unset, and being billed with no GST at all today. This
	// is the half that is unambiguous: every one is an increase from zero, so
	// nothing is being under-collected on the strength of a script's opinion.
	{7074, "Amla Dabur Oral Care Chewing Gum 10g", 18, "chewing gum, 18%", true},
	{7075, "Cheetos Chips 100g", 12, "packaged extruded snack, 12%", true},
	{7076, "Cheerios Breakfast Cereal 100g", 18, "packaged cereal, 18%", true},
	{7077, "Hot Heads 30g", 12, "packaged snack, 12%", true},
}

func main() {
	mode := "plan"
	if len(os.Args) > 1 {
		mode = os.Args[1]
	}

	_ = godotenv.Load()
	dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
		os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"),
		os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME"))

	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		log.Fatal(err)
	}

	write := mode == "apply"

	fmt.Printf("%-6s %-38s %6s -> %6s   %s\n", "id", "product", "now", "new", "why")
	fmt.Println("-------------------------------------------------------------------------------------------")

	undo := []string{}
	changes := 0
	review := 0

	for _, r := range rates {
		var current struct {
			Taxpercent float64
			Found      bool
		}
		if err := db.Raw(`SELECT COALESCE(taxpercent, 0) AS taxpercent, true AS found
		                  FROM products WHERE productid = ? LIMIT 1`,
			r.productID).Scan(&current).Error; err != nil {
			log.Fatalf("reading %d: %v", r.productID, err)
		}
		if !current.Found {
			fmt.Printf("%-6d %-38s  NO products ROW - skipped\n", r.productID, r.name)
			continue
		}

		if current.Taxpercent == r.percent {
			fmt.Printf("%-6d %-38s %6.0f      unchanged         %s\n",
				r.productID, r.name, current.Taxpercent, r.why)
			continue
		}

		if !r.apply {
			fmt.Printf("%-6d %-38s %6.0f   REVIEW %-3.0f   %s\n",
				r.productID, r.name, current.Taxpercent, r.percent, r.why)
			review++
			continue
		}

		fmt.Printf("%-6d %-38s %6.0f -> %6.0f   %s\n",
			r.productID, r.name, current.Taxpercent, r.percent, r.why)

		undo = append(undo, fmt.Sprintf(
			"UPDATE products SET taxpercent = %.0f WHERE productid = %d;",
			current.Taxpercent, r.productID))
		changes++

		if write {
			// updated is bumped so the catalogue delta carries the new rate to
			// terminals holding a revision, rather than waiting for a full pull.
			if err := db.Exec(`UPDATE products SET taxpercent = ?, updated = NOW()
			                   WHERE productid = ?`, r.percent, r.productID).Error; err != nil {
				log.Fatalf("writing %d: %v", r.productID, err)
			}
		}
	}

	fmt.Println("-------------------------------------------------------------------------------------------")
	if write {
		fmt.Printf("APPLIED %d rate(s).\n", changes)
	} else {
		fmt.Printf("%d rate(s) would change. Nothing written — run `apply` to commit.\n", changes)
	}
	if review > 0 {
		fmt.Printf("%d row(s) flagged REVIEW and deliberately not written — each is a\n"+
			"reduction of a live tax rate and needs a decision, not a script.\n", review)
	}
	fmt.Println()

	if len(undo) > 0 {
		fmt.Println("-- undo:")
		for _, u := range undo {
			fmt.Println(u)
		}
	}
}
