package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"os"
	"time"

	"github.com/gofrs/uuid/v5"
	_ "github.com/jackc/pgx/v5/stdlib"
	"github.com/jmoiron/sqlx"
	"github.com/joho/godotenv"
)

// Anápolis, GO coordinates
const (
	AnapolisLat = -16.3281
	AnapolisLng = -48.9530
	VarianceKm  = 5.0 // ~5km spread
)

// Product categories with sample medicines
var categories = []string{
	"Analgésicos",
	"Antibióticos",
	"Anti-inflamatórios",
	"Cardiovasculares",
	"Dermatológicos",
	"Vitaminas",
	"Oftálmicos",
	"Respiratórios",
}

var medicamentos = []struct {
	Name      string
	Category  string
	BasePrice int64
}{
	{"Dipirona 500mg", "Analgésicos", 1299},
	{"Paracetamol 750mg", "Analgésicos", 899},
	{"Ibuprofeno 400mg", "Anti-inflamatórios", 1599},
	{"Nimesulida 100mg", "Anti-inflamatórios", 2499},
	{"Amoxicilina 500mg", "Antibióticos", 3499},
	{"Azitromicina 500mg", "Antibióticos", 4999},
	{"Losartana 50mg", "Cardiovasculares", 2199},
	{"Atenolol 50mg", "Cardiovasculares", 1899},
	{"Vitamina C 1g", "Vitaminas", 1599},
	{"Vitamina D 2000UI", "Vitaminas", 2299},
	{"Cetoconazol Creme", "Dermatológicos", 2899},
	{"Dexametasona Creme", "Dermatológicos", 1999},
	{"Colírio Lubrificante", "Oftálmicos", 3299},
	{"Lacrifilm 15ml", "Oftálmicos", 2899},
	{"Loratadina 10mg", "Respiratórios", 1799},
	{"Dexclorfeniramina 2mg", "Respiratórios", 1599},
	{"Omeprazol 20mg", "Gastrointestinais", 1999},
	{"Pantoprazol 40mg", "Gastrointestinais", 2999},
	{"Metformina 850mg", "Antidiabéticos", 1299},
	{"Glibenclamida 5mg", "Antidiabéticos", 999},
}

var pharmacyNames = []string{
	"Farmácia Popular", "Drogaria Central", "Farma Vida", "Drogasil",
	"Farmácia São João", "Droga Raia", "Ultrafarma", "Farma Bem",
	"Drogaria Moderna", "Farmácia União", "Saúde Total", "Bem Estar",
	"Vida Saudável", "Mais Saúde", "Farmácia do Povo", "Super Farma",
}

func main() {
	godotenv.Load()
	dsn := os.Getenv("DATABASE_URL")
	if dsn == "" {
		log.Fatal("DATABASE_URL not set")
	}

	db, err := sqlx.Connect("pgx", dsn)
	if err != nil {
		log.Fatalf("db connect: %v", err)
	}
	defer db.Close()

	ctx := context.Background()
	rng := rand.New(rand.NewSource(time.Now().UnixNano()))

	// Re-create tables to ensure schema match
	log.Println("Re-creating tables...")
	mustExec(db, `DROP TABLE IF EXISTS inventory_adjustments CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS order_items CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS orders CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS cart_items CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS reviews CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS products CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS users CASCADE`)
	mustExec(db, `DROP TABLE IF EXISTS companies CASCADE`)

	mustExec(db, `CREATE TABLE companies (
		id UUID PRIMARY KEY,
		cnpj TEXT NOT NULL UNIQUE,
		corporate_name TEXT NOT NULL,
		category TEXT NOT NULL DEFAULT 'farmacia',
		license_number TEXT NOT NULL,
		is_verified BOOLEAN NOT NULL DEFAULT FALSE,
		latitude DOUBLE PRECISION NOT NULL DEFAULT 0,
		longitude DOUBLE PRECISION NOT NULL DEFAULT 0,
		city TEXT NOT NULL DEFAULT '',
		state TEXT NOT NULL DEFAULT '',
		created_at TIMESTAMPTZ NOT NULL,
		updated_at TIMESTAMPTZ NOT NULL
	)`)

	mustExec(db, `CREATE TABLE products (
		id UUID PRIMARY KEY,
		seller_id UUID NOT NULL REFERENCES companies(id),
		name TEXT NOT NULL,
		description TEXT,
		batch TEXT NOT NULL,
		expires_at DATE NOT NULL,
		price_cents BIGINT NOT NULL,
		stock BIGINT NOT NULL,
		created_at TIMESTAMPTZ NOT NULL,
		updated_at TIMESTAMPTZ NOT NULL
	)`)

	// Generate 400 tenants
	tenants := generateTenants(rng, 400)
	log.Printf("Generated %d tenants", len(tenants))

	// Insert tenants
	for _, t := range tenants {
		_, err := db.NamedExecContext(ctx, `
			INSERT INTO companies (id, cnpj, corporate_name, category, license_number, is_verified, latitude, longitude, city, state, created_at, updated_at)
			VALUES (:id, :cnpj, :corporate_name, :category, :license_number, :is_verified, :latitude, :longitude, :city, :state, :created_at, :updated_at)
			ON CONFLICT (cnpj) DO NOTHING`, t)
		if err != nil {
			log.Printf("insert tenant %s: %v", t["corporate_name"], err)
		}
	}
	log.Println("Tenants inserted")

	// Generate and insert products for each tenant
	totalProducts := 0
	for _, t := range tenants {
		tenantID := t["id"].(uuid.UUID)
		numProducts := 20 + rng.Intn(481) // 20-500
		products := generateProducts(rng, tenantID, numProducts)
		totalProducts += len(products)

		for _, p := range products {
			_, err := db.NamedExecContext(ctx, `
				INSERT INTO products (id, seller_id, name, description, batch, expires_at, price_cents, stock, created_at, updated_at)
				VALUES (:id, :seller_id, :name, :description, :batch, :expires_at, :price_cents, :stock, :created_at, :updated_at)
				ON CONFLICT DO NOTHING`, p)
			if err != nil {
				log.Printf("insert product: %v", err)
			}
		}
	}
	log.Printf("Inserted %d products total", totalProducts)

	// Output summary
	summary := map[string]interface{}{
		"tenants":  len(tenants),
		"products": totalProducts,
		"location": "Anápolis, GO",
	}
	out, _ := json.MarshalIndent(summary, "", "  ")
	fmt.Println(string(out))
}

func generateTenants(rng *rand.Rand, count int) []map[string]interface{} {
	now := time.Now().UTC()
	tenants := make([]map[string]interface{}, 0, count)

	for i := 0; i < count; i++ {
		id, _ := uuid.NewV4()
		name := fmt.Sprintf("%s %d", pharmacyNames[rng.Intn(len(pharmacyNames))], i+1)
		cnpj := generateCNPJ(rng)

		// Vary lat/lng within ~5km of Anápolis center
		latOffset := (rng.Float64() - 0.5) * 0.09 // ~5km
		lngOffset := (rng.Float64() - 0.5) * 0.09

		tenants = append(tenants, map[string]interface{}{
			"id":             id,
			"cnpj":           cnpj,
			"corporate_name": name,
			"category":       "farmacia",
			"license_number": fmt.Sprintf("CRF-GO-%05d", rng.Intn(99999)),
			"is_verified":    rng.Float32() > 0.3, // 70% verified
			"latitude":       AnapolisLat + latOffset,
			"longitude":      AnapolisLng + lngOffset,
			"city":           "Anápolis",
			"state":          "GO",
			"created_at":     now,
			"updated_at":     now,
		})
	}
	return tenants
}

func generateProducts(rng *rand.Rand, sellerID uuid.UUID, count int) []map[string]interface{} {
	now := time.Now().UTC()
	products := make([]map[string]interface{}, 0, count)

	for i := 0; i < count; i++ {
		id, _ := uuid.NewV4()
		med := medicamentos[rng.Intn(len(medicamentos))]

		// Random expiration: 30 days to 2 years from now
		daysToExpire := 30 + rng.Intn(700)
		expiresAt := now.AddDate(0, 0, daysToExpire)

		// Price variation: -20% to +30%
		priceMultiplier := 0.8 + rng.Float64()*0.5
		price := int64(float64(med.BasePrice) * priceMultiplier)

		products = append(products, map[string]interface{}{
			"id":          id,
			"seller_id":   sellerID,
			"name":        med.Name,
			"description": fmt.Sprintf("%s - %s", med.Name, med.Category),
			"batch":       fmt.Sprintf("L%d%03d", now.Year(), rng.Intn(999)),
			"expires_at":  expiresAt,
			"price_cents": price,
			"stock":       int64(10 + rng.Intn(500)),
			"created_at":  now,
			"updated_at":  now,
		})
	}
	return products
}

func generateCNPJ(rng *rand.Rand) string {
	return fmt.Sprintf("%02d.%03d.%03d/%04d-%02d",
		rng.Intn(99), rng.Intn(999), rng.Intn(999),
		rng.Intn(9999)+1, rng.Intn(99))
}

func mustExec(db *sqlx.DB, query string) {
	_, err := db.Exec(query)
	if err != nil {
		log.Fatalf("exec %s: %v", query, err)
	}
}
