package main

import (
	"encoding/json"
	"fmt"
	"slices"
	"sync"
	"time"

	messages "git.tornberg.me/go-cart-actor/pkg/messages"
)

// Legacy padded [16]byte CartId and its helper methods removed.
// Unified CartId (uint64 with base62 string form) now defined in cart_id.go.

type StockStatus int

const (
	OutOfStock StockStatus = 0
	LowStock   StockStatus = 1
	InStock    StockStatus = 2
)

type CartItem struct {
	Id          int         `json:"id"`
	ItemId      int         `json:"itemId,omitempty"`
	ParentId    int         `json:"parentId,omitempty"`
	Sku         string      `json:"sku"`
	Name        string      `json:"name"`
	Price       int64       `json:"price"`
	TotalPrice  int64       `json:"totalPrice"`
	TotalTax    int64       `json:"totalTax"`
	OrgPrice    int64       `json:"orgPrice"`
	Stock       StockStatus `json:"stock"`
	Quantity    int         `json:"qty"`
	Tax         int         `json:"tax"`
	TaxRate     int         `json:"taxRate"`
	Brand       string      `json:"brand,omitempty"`
	Category    string      `json:"category,omitempty"`
	Category2   string      `json:"category2,omitempty"`
	Category3   string      `json:"category3,omitempty"`
	Category4   string      `json:"category4,omitempty"`
	Category5   string      `json:"category5,omitempty"`
	Disclaimer  string      `json:"disclaimer,omitempty"`
	SellerId    string      `json:"sellerId,omitempty"`
	SellerName  string      `json:"sellerName,omitempty"`
	ArticleType string      `json:"type,omitempty"`
	Image       string      `json:"image,omitempty"`
	Outlet      *string     `json:"outlet,omitempty"`
	StoreId     *string     `json:"storeId,omitempty"`
}

type CartDelivery struct {
	Id          int                   `json:"id"`
	Provider    string                `json:"provider"`
	Price       int64                 `json:"price"`
	Items       []int                 `json:"items"`
	PickupPoint *messages.PickupPoint `json:"pickupPoint,omitempty"`
}

type CartGrain struct {
	mu                sync.RWMutex
	lastItemId        int
	lastDeliveryId    int
	lastAccess        time.Time
	lastChange        time.Time       // unix seconds of last successful mutation (replay sets from event ts)
	Id                CartId          `json:"id"`
	Items             []*CartItem     `json:"items"`
	TotalPrice        int64           `json:"totalPrice"`
	TotalTax          int64           `json:"totalTax"`
	TotalDiscount     int64           `json:"totalDiscount"`
	Deliveries        []*CartDelivery `json:"deliveries,omitempty"`
	Processing        bool            `json:"processing"`
	PaymentInProgress bool            `json:"paymentInProgress"`
	OrderReference    string          `json:"orderReference,omitempty"`
	PaymentStatus     string          `json:"paymentStatus,omitempty"`
}

func (c *CartGrain) GetId() uint64 {
	return uint64(c.Id)
}

func (c *CartGrain) GetLastChange() time.Time {
	return c.lastChange
}

func (c *CartGrain) GetLastAccess() time.Time {
	return c.lastAccess
}

func (c *CartGrain) GetCurrentState() (*CartGrain, error) {
	c.lastAccess = time.Now()
	return c, nil
}

func getInt(data float64, ok bool) (int, error) {
	if !ok {
		return 0, fmt.Errorf("invalid type")
	}
	return int(data), nil
}

func GetItemAddMessage(sku string, qty int, country string, storeId *string) (*messages.AddItem, error) {
	item, err := FetchItem(sku, country)
	if err != nil {
		return nil, err
	}
	orgPrice, _ := getInt(item.GetNumberFieldValue(5)) // getInt(item.Fields[5])

	price, priceErr := getInt(item.GetNumberFieldValue(4)) //Fields[4]

	if priceErr != nil {
		return nil, fmt.Errorf("invalid price")
	}

	stock := InStock
	item.HasStock()
	stockValue, ok := item.GetNumberFieldValue(3)
	if !ok || stockValue == 0 {
		stock = OutOfStock
	} else {
		if stockValue < 5 {
			stock = LowStock
		}
	}

	articleType, _ := item.GetStringFieldValue(1)   //.Fields[1].(string)
	outletGrade, ok := item.GetStringFieldValue(20) //.Fields[20].(string)
	var outlet *string
	if ok {
		outlet = &outletGrade
	}
	sellerId, _ := item.GetStringFieldValue(24)  // .Fields[24].(string)
	sellerName, _ := item.GetStringFieldValue(9) // .Fields[9].(string)

	brand, _ := item.GetStringFieldValue(2)      //.Fields[2].(string)
	category, _ := item.GetStringFieldValue(10)  //.Fields[10].(string)
	category2, _ := item.GetStringFieldValue(11) //.Fields[11].(string)
	category3, _ := item.GetStringFieldValue(12) //.Fields[12].(string)
	category4, _ := item.GetStringFieldValue(13) //Fields[13].(string)
	category5, _ := item.GetStringFieldValue(14) //.Fields[14].(string)

	return &messages.AddItem{
		ItemId:      int64(item.Id),
		Quantity:    int32(qty),
		Price:       int64(price),
		OrgPrice:    int64(orgPrice),
		Sku:         sku,
		Name:        item.Title,
		Image:       item.Img,
		Stock:       int32(stock),
		Brand:       brand,
		Category:    category,
		Category2:   category2,
		Category3:   category3,
		Category4:   category4,
		Category5:   category5,
		Tax:         2500,
		SellerId:    sellerId,
		SellerName:  sellerName,
		ArticleType: articleType,
		Disclaimer:  item.Disclaimer,
		Country:     country,
		Outlet:      outlet,
		StoreId:     storeId,
	}, nil
}

// func (c *CartGrain) AddItem(sku string, qty int, country string, storeId *string) (*CartGrain, error) {
// 	cartItem, err := getItemData(sku, qty, country)
// 	if err != nil {
// 		return nil, err
// 	}
// 	cartItem.StoreId = storeId
// 	return c.Apply(cartItem, false)
// }

func (c *CartGrain) GetState() ([]byte, error) {
	return json.Marshal(c)
}

func (c *CartGrain) ItemsWithDelivery() []int {
	ret := make([]int, 0, len(c.Items))
	for _, item := range c.Items {
		for _, delivery := range c.Deliveries {
			for _, id := range delivery.Items {
				if item.Id == id {
					ret = append(ret, id)
				}
			}
		}
	}
	return ret
}

func (c *CartGrain) ItemsWithoutDelivery() []int {
	ret := make([]int, 0, len(c.Items))
	hasDelivery := c.ItemsWithDelivery()
	for _, item := range c.Items {
		found := slices.Contains(hasDelivery, item.Id)

		if !found {
			ret = append(ret, item.Id)
		}
	}
	return ret
}

func (c *CartGrain) FindItemWithSku(sku string) (*CartItem, bool) {
	c.mu.RLock()
	defer c.mu.RUnlock()
	for _, item := range c.Items {
		if item.Sku == sku {
			return item, true
		}
	}
	return nil, false
}

func GetTaxAmount(total int64, tax int) int64 {
	taxD := 10000 / float64(tax)
	return int64(float64(total) / float64((1 + taxD)))
}

// func (c *CartGrain) Apply(content proto.Message, isReplay bool) (*CartGrain, error) {

// 	updated, err := ApplyRegistered(c, content)
// 	if err != nil {
// 		if err == ErrMutationNotRegistered {
// 			return nil, fmt.Errorf("unsupported mutation type %T (not registered)", content)
// 		}
// 		return nil, err
// 	}

// 	// Sliding TTL: update lastChange only for non-replay successful mutations.
// 	if updated != nil && !isReplay {
// 		c.lastChange = time.Now()
// 		c.lastAccess = time.Now()
// 		go AppendCartEvent(c.Id, content)
// 	}

// 	return updated, nil
// }

func (c *CartGrain) UpdateTotals() {
	c.TotalPrice = 0
	c.TotalTax = 0
	c.TotalDiscount = 0
	for _, item := range c.Items {
		rowTotal := item.Price * int64(item.Quantity)
		rowTax := int64(item.Tax) * int64(item.Quantity)
		item.TotalPrice = rowTotal
		item.TotalTax = rowTax
		c.TotalPrice += rowTotal
		c.TotalTax += rowTax
		itemDiff := max(0, item.OrgPrice-item.Price)
		c.TotalDiscount += itemDiff * int64(item.Quantity)
	}
	for _, delivery := range c.Deliveries {
		c.TotalPrice += delivery.Price
		c.TotalTax += GetTaxAmount(delivery.Price, 2500)
	}

}
