package seed

import (
	"errors"
	"fmt"
	"strings"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"

	"gorm.io/gorm"
)

func Run(db *gorm.DB) error {
	return db.Transaction(func(tx *gorm.DB) error {
		users, err := seedUsers(tx)
		if err != nil {
			return err
		}
		adminID := users["admin"]

		uoms, err := seedUoms(tx, adminID)
		if err != nil {
			return err
		}

		productCategories, err := seedProductCategories(tx, adminID)
		if err != nil {
			return err
		}

		suppliers, err := seedSuppliers(tx, adminID)
		if err != nil {
			return err
		}

		if err := seedProducts(tx, adminID, uoms, productCategories, suppliers); err != nil {
			return err
		}

		fmt.Println("✅ Master data seeding completed")
		return nil
	})
}

func seedUsers(tx *gorm.DB) (map[string]uint, error) {
	seeds := []struct {
		Key  string
		Data entity.User
	}{
		{
			Key:  "admin",
			Data: entity.User{Email: "admin@mbugroup.id", IdUser: 1, Name: "Super Admin"},
		},
	}

	result := make(map[string]uint, len(seeds))

	for _, seed := range seeds {
		var user entity.User
		err := tx.Where("email = ?", seed.Data.Email).First(&user).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			user = seed.Data
			if err := tx.Create(&user).Error; err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		}
		result[seed.Key] = user.Id
	}

	return result, nil
}

func seedUoms(tx *gorm.DB, createdBy uint) (map[string]uint, error) {
	names := []string{"Kilogram", "Gram", "Liter", "Unit", "Ekor", "Butir"}
	result := make(map[string]uint, len(names))

	for _, name := range names {
		var uom entity.Uom
		err := tx.Where("name = ?", name).First(&uom).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			uom = entity.Uom{Name: name, CreatedBy: createdBy}
			if err := tx.Create(&uom).Error; err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		}
		result[name] = uom.Id
	}

	return result, nil
}

func seedProductCategories(tx *gorm.DB, createdBy uint) (map[string]uint, error) {
	seeds := []struct {
		Name string
		Code string
	}{
		{"Pullet", "PLT"},
		{"Bahan Baku", "RAW"},
		{"Day Old Chick", "DOC"},
		{"Telur", "EGG"},
	}

	result := make(map[string]uint, len(seeds))

	for _, seed := range seeds {
		var category entity.ProductCategory
		err := tx.Where("name = ?", seed.Name).First(&category).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			category = entity.ProductCategory{Name: seed.Name, Code: seed.Code, CreatedBy: createdBy}
			if err := tx.Create(&category).Error; err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		} else {
			if err := tx.Model(&entity.ProductCategory{}).Where("id = ?", category.Id).Updates(map[string]any{
				"code": seed.Code,
			}).Error; err != nil {
				return nil, err
			}
		}
		result[seed.Name] = category.Id
	}

	return result, nil
}

func seedSuppliers(tx *gorm.DB, createdBy uint) (map[string]uint, error) {
	seeds := []struct {
		Name     string
		Alias    string
		Category string
		Email    string
		Phone    string
		Address  string
	}{
		{"PT CHAROEN POKPHAND INDONESIA Tbk", "CPI", string(utils.SupplierCategorySapronak), "cpi@gmail.com", "081200000001", "Jl. Pakan 1, Bekasi"},
		{"BOP Vendor", "BOP", string(utils.SupplierCategoryBOP), "bop@gmail.com", "081200000002", "Jl. Veteriner 3, Bogor"},
		{"Ekspedisi", "EKS", string(utils.SupplierCategoryBOP), "bop@gmail.com", "081200000002", "Jl. Veteriner 3, Bogor"},
	}

	result := make(map[string]uint, len(seeds))

	for idx, seed := range seeds {
		var supplier entity.Supplier
		err := tx.Where("name = ?", seed.Name).First(&supplier).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			supplier = entity.Supplier{
				Name:          seed.Name,
				Alias:         seed.Alias,
				Pic:           "John Doe",
				Type:          string(utils.CustomerSupplierTypeBisnis),
				Category:      seed.Category,
				Phone:         seed.Phone,
				Email:         seed.Email,
				Address:       seed.Address,
				DueDate:       30,
				CreatedBy:     createdBy,
				AccountNumber: strPtr(fmt.Sprintf("%03d", idx+1)),
			}
			if err := tx.Create(&supplier).Error; err != nil {
				return nil, err
			}
		} else if err != nil {
			return nil, err
		}
		result[seed.Name] = supplier.Id
	}

	return result, nil
}

func seedProducts(tx *gorm.DB, createdBy uint, uoms map[string]uint, categories map[string]uint, suppliers map[string]uint) error {
	seeds := []struct {
		Name      string
		Brand     string
		Sku       string
		Uom       string
		Category  string
		Price     float64
		Selling   *float64
		Tax       *float64
		Expiry    *int
		Suppliers []string
		Flags     []utils.FlagType
		IsVisible bool
	}{
		{
			Name:      "ISA Brown",
			Brand:     "ISA Brown",
			Sku:       "ISA0001",
			Uom:       "Ekor",
			Category:  "Day Old Chick",
			Price:     7500,
			Suppliers: []string{"PT CHAROEN POKPHAND INDONESIA Tbk"},
			Flags:     []utils.FlagType{utils.FlagDOC, utils.FlagPullet, utils.FlagLayer},
			IsVisible: true,
		},
		{
			Name:      "Ayam Afkir",
			Brand:     "-",
			Sku:       "1",
			Uom:       "Ekor",
			Category:  "Day Old Chick",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagAyamAfkir},
			IsVisible: false,
		},
		{
			Name:      "Ayam Mati",
			Brand:     "-",
			Sku:       "2",
			Uom:       "Ekor",
			Category:  "Day Old Chick",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagAyamMati},
			IsVisible: false,
		},
		{
			Name:      "Ayam Culling",
			Brand:     "-",
			Sku:       "3",
			Uom:       "Ekor",
			Category:  "Day Old Chick",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagAyamCulling},
			IsVisible: false,
		},
		{
			Name:      "Telur Utuh",
			Brand:     "-",
			Sku:       "4",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelurUtuh},
			IsVisible: false,
		},
		{
			Name:      "Telur Pecah",
			Brand:     "-",
			Sku:       "5",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelurPecah},
			IsVisible: false,
		},
		{
			Name:      "Telur Putih",
			Brand:     "-",
			Sku:       "6",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelurPutih},
			IsVisible: false,
		},
		{
			Name:      "Telur Retak",
			Brand:     "-",
			Sku:       "7",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelurRetak},
			IsVisible: false,
		},
		{
			Name:      "Telur Papacal",
			Brand:     "-",
			Sku:       "8",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelur},
			IsVisible: false,
		},
		{
			Name:      "Telur Jumbo",
			Brand:     "-",
			Sku:       "9",
			Uom:       "Butir",
			Category:  "Telur",
			Price:     1,
			Flags:     []utils.FlagType{utils.FlagTelur},
			IsVisible: false,
		},
	}

	for _, seed := range seeds {
		uomID, ok := uoms[seed.Uom]
		if !ok {
			return fmt.Errorf("uom %s not seeded", seed.Uom)
		}
		categoryID, ok := categories[seed.Category]
		if !ok {
			return fmt.Errorf("product category %s not seeded", seed.Category)
		}

		var product entity.Product
		err := tx.Where("name = ?", seed.Name).First(&product).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			selling := seed.Selling
			tax := seed.Tax
			product = entity.Product{
				Name:              seed.Name,
				Brand:             seed.Brand,
				Sku:               &seed.Sku,
				UomId:             uomID,
				ProductCategoryId: categoryID,
				ProductPrice:      seed.Price,
				SellingPrice:      selling,
				Tax:               tax,
				ExpiryPeriod:      seed.Expiry,
				CreatedBy:         createdBy,
				IsVisible:         seed.IsVisible,
			}
			if err := tx.Create(&product).Error; err != nil {
				return err
			}
		} else if err != nil {
			return err
		} else {
			updates := map[string]any{
				"brand":               seed.Brand,
				"uom_id":              uomID,
				"product_category_id": categoryID,
				"product_price":       seed.Price,
				"selling_price":       seed.Selling,
				"tax":                 seed.Tax,
				"expiry_period":       seed.Expiry,
			}
			if seed.Sku != "" {
				updates["sku"] = seed.Sku
			}
			if err := tx.Model(&entity.Product{}).Where("id = ?", product.Id).Updates(updates).Error; err != nil {
				return err
			}
		}

		for _, supplierName := range seed.Suppliers {
			supplierID, ok := suppliers[supplierName]
			if !ok {
				return fmt.Errorf("supplier %s not seeded", supplierName)
			}
			var existing entity.ProductSupplier
			err := tx.Where("product_id = ? AND supplier_id = ?", product.Id, supplierID).First(&existing).Error
			if errors.Is(err, gorm.ErrRecordNotFound) {
				link := entity.ProductSupplier{ProductId: product.Id, SupplierId: supplierID}
				if err := tx.Create(&link).Error; err != nil {
					return err
				}
			} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
				return err
			}
		}

		if err := seedFlags(tx, product.Id, entity.FlagableTypeProduct, seed.Flags); err != nil {
			return err
		}
	}

	return nil
}

// func seedNonstocks(tx *gorm.DB, createdBy uint, uoms map[string]uint, suppliers map[string]uint) error {
// 	seeds := []struct {
// 		Name      string
// 		Uom       string
// 		Suppliers []string
// 		Flags     []utils.FlagType
// 	}{
// 		{
// 			Name:      "LAJ",
// 			Uom:       "Unit",
// 			Suppliers: []string{"Ekspedisi"},
// 			Flags:     []utils.FlagType{utils.FlagEkspedisi},
// 		},
// 		{
// 			Name:      "Solar",
// 			Uom:       "Liter",
// 			Suppliers: []string{"BOP Vendor"},
// 			Flags:     []utils.FlagType{},
// 		},
// 	}

// 	for _, seed := range seeds {
// 		uomID, ok := uoms[seed.Uom]
// 		if !ok {
// 			return fmt.Errorf("uom %s not seeded", seed.Uom)
// 		}

// 		var nonstock entity.Nonstock
// 		err := tx.Where("name = ?", seed.Name).First(&nonstock).Error
// 		if errors.Is(err, gorm.ErrRecordNotFound) {
// 			nonstock = entity.Nonstock{
// 				Name:      seed.Name,
// 				UomId:     uomID,
// 				CreatedBy: createdBy,
// 			}
// 			if err := tx.Create(&nonstock).Error; err != nil {
// 				return err
// 			}
// 		} else if err != nil {
// 			return err
// 		} else {
// 			if err := tx.Model(&entity.Nonstock{}).Where("id = ?", nonstock.Id).Updates(map[string]any{
// 				"uom_id": uomID,
// 			}).Error; err != nil {
// 				return err
// 			}
// 		}

// 		for _, supplierName := range seed.Suppliers {
// 			supplierID, ok := suppliers[supplierName]
// 			if !ok {
// 				return fmt.Errorf("supplier %s not seeded", supplierName)
// 			}
// 			var existing entity.NonstockSupplier
// 			err := tx.Where("nonstock_id = ? AND supplier_id = ?", nonstock.Id, supplierID).First(&existing).Error
// 			if errors.Is(err, gorm.ErrRecordNotFound) {
// 				link := entity.NonstockSupplier{NonstockId: nonstock.Id, SupplierId: supplierID}
// 				if err := tx.Create(&link).Error; err != nil {
// 					return err
// 				}
// 			} else if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
// 				return err
// 			}
// 		}

// 		if err := seedFlags(tx, nonstock.Id, entity.FlagableTypeNonstock, seed.Flags); err != nil {
// 			return err
// 		}
// 	}

// 	return nil
// }

// nanti saya isi

func seedFlags(tx *gorm.DB, flagableID uint, flagableType string, flags []utils.FlagType) error {
	if len(flags) == 0 {
		return nil
	}
	for _, flag := range flags {
		name := strings.ToUpper(string(flag))
		var existing entity.Flag
		err := tx.Where("name = ? AND flagable_id = ? AND flagable_type = ?", name, flagableID, flagableType).First(&existing).Error
		if errors.Is(err, gorm.ErrRecordNotFound) {
			record := entity.Flag{
				Name:         name,
				FlagableID:   flagableID,
				FlagableType: flagableType,
			}
			if err := tx.Create(&record).Error; err != nil {
				return err
			}
		} else if err != nil {
			return err
		}
	}
	return nil
}

func strPtr(s string) *string {
	return &s
}
