// Seed retail prices so the POS has something sellable.
//
// These are plausible Coimbatore figures, not authoritative ones. They exist so
// the terminal can ring a real bill; the owner corrects them afterwards.
//
//	go run ./scratch/seedprices plan     # show every change and the undo SQL
//	go run ./scratch/seedprices apply    # write them
//	go run ./scratch/seedprices verify   # read back what the catalogue now serves
package main

import (
	"fmt"
	"log"
	"os"

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

type priced struct {
	locationID int
	productID  int
	name       string
	unit       string
	price      float64
	note       string
}

// Prices are per the product's own unit — per kilogram where the unit is
// kilogram, per pack where it is piece. Getting that backwards is the easiest
// way to make a till look broken, so the unit is carried through and printed.
var seed = []priced{
	// 1135 — fresh produce
	{1135, 6988, "Mysore Banana", "kilogram", 60, ""},
	{1135, 6989, "Jammu Apple", "piece", 30, "per fruit, not per kg"},
	{1135, 6990, "Small orange", "kilogram", 90, ""},
	{1135, 6991, "Red Guava", "kilogram", 80, ""},
	{1135, 6992, "Pomegrante", "kilogram", 180, ""},
	{1135, 6993, "Salem Mango", "kilogram", 90, "seasonal, swings 80-120"},
	{1135, 6994, "Pineapple", "kilogram", 60, ""},
	{1135, 6995, "Strawberries", "piece", 150, "priced as a punnet"},
	{1135, 6996, "Maceral", "kilogram", 220, "READ AS MACKEREL - correct if wrong"},
	{1135, 6997, "Tuna", "kilogram", 280, ""},
	{1135, 6998, "Hatsun curd", "piece", 30, "500g pouch"},
	{1135, 7014, "Apple", "kilogram", 200, ""},

	// 1185 — packaged
	{1185, 7074, "Amla Dabur Oral Care Chewing Gum 10g", "piece", 10, ""},
	{1185, 7075, "Cheetos Chips 100g", "piece", 40, ""},
	{1185, 7076, "Cheerios Breakfast Cereal 100g", "piece", 120, ""},
	// 7077 Hot Heads is already at 50 — someone set it deliberately, leave it.
}

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

	switch mode {
	case "plan":
		plan(db, false)
	case "apply":
		plan(db, true)
	case "verify":
		verify(db)
	default:
		log.Fatalf("unknown mode %q — use plan, apply or verify", mode)
	}
}

func plan(db *gorm.DB, write bool) {
	fmt.Printf("%-6s %-38s %-9s %8s -> %8s\n", "id", "product", "unit", "now", "new")
	fmt.Println("--------------------------------------------------------------------------------")

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

	for _, p := range seed {
		var current struct {
			Price    float64
			Tenantid int
			Found    bool
		}
		row := db.Raw(`SELECT COALESCE(price, 0) AS price, tenantid, true AS found
		               FROM productlocations
		               WHERE productid = ? AND locationid = ?
		               LIMIT 1`, p.productID, p.locationID).Scan(&current)
		if row.Error != nil {
			log.Fatalf("reading %d: %v", p.productID, row.Error)
		}
		if !current.Found {
			fmt.Printf("%-6d %-38s  NO productlocations ROW - skipped\n", p.productID, p.name)
			continue
		}

		// Never overwrite a price a human already set. A seed value is a
		// placeholder; a real one is a decision, and losing it silently would
		// be worse than leaving a gap.
		if current.Price > 0 {
			fmt.Printf("%-6d %-38s %-9s %8.2f    already priced, left alone\n",
				p.productID, p.name, p.unit, current.Price)
			continue
		}

		note := ""
		if p.note != "" {
			note = "   <- " + p.note
		}
		fmt.Printf("%-6d %-38s %-9s %8.2f -> %8.2f%s\n",
			p.productID, p.name, p.unit, current.Price, p.price, note)

		undo = append(undo, fmt.Sprintf(
			"UPDATE productlocations SET price = %.2f WHERE productid = %d AND locationid = %d;",
			current.Price, p.productID, p.locationID))
		changes++

		if write {
			// updated is bumped so the catalogue delta carries the new price to
			// terminals that already hold a revision, rather than waiting for
			// someone to force a full pull.
			err := db.Exec(`UPDATE productlocations
			                SET price = ?, updated = NOW()
			                WHERE productid = ? AND locationid = ?`,
				p.price, p.productID, p.locationID).Error
			if err != nil {
				log.Fatalf("writing %d: %v", p.productID, err)
			}
		}
	}

	fmt.Println("--------------------------------------------------------------------------------")
	if write {
		fmt.Printf("APPLIED %d price(s).\n\n", changes)
	} else {
		fmt.Printf("%d price(s) would change. Nothing written — run `apply` to commit.\n\n", changes)
	}

	fmt.Println("-- undo, if you want the zeros back:")
	for _, u := range undo {
		fmt.Println(u)
	}
}

func verify(db *gorm.DB) {
	type row struct {
		Locationid  int
		Productid   int
		Productname string
		Price       float64
		Taxpercent  float64
	}
	var rows []row
	db.Raw(`SELECT b.locationid, a.productid, a.productname,
	               COALESCE(b.price, 0) AS price, COALESCE(a.taxpercent, 0) AS taxpercent
	        FROM products a
	        INNER JOIN productlocations b ON a.productid = b.productid AND a.tenantid = b.tenantid
	        WHERE b.locationid IN (1135, 1185) AND a.productid > 0
	        ORDER BY b.locationid, a.productid`).Scan(&rows)

	sellable := 0
	for _, r := range rows {
		flag := ""
		if r.Price > 0 {
			sellable++
		} else {
			flag = "  <- still zero, not sellable"
		}
		fmt.Printf("loc %d  %-6d %-38s %8.2f  tax=%.0f%s\n",
			r.Locationid, r.Productid, r.Productname[:min(38, len(r.Productname))], r.Price, r.Taxpercent, flag)
	}
	fmt.Printf("\n%d of %d rows are sellable.\n", sellable, len(rows))
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
