package repository

import (
	"context"
	_ "embed"
	"encoding/json"
	"sync"

	"route256/loms/internal/domain/entity"
	"route256/loms/internal/domain/model"
)

//go:embed stock-data.json
var stockData []byte

type storage = map[entity.Sku]*entity.Stock

type InMemoryRepository struct {
	storage storage
	mx      sync.RWMutex
}

func NewInMemoryRepository(cap int) (*InMemoryRepository, error) {
	var rows []struct {
		Sku        entity.Sku `json:"sku"`
		TotalCount uint32     `json:"total_count"`
		Reserved   uint32     `json:"reserved"`
	}

	if err := json.Unmarshal(stockData, &rows); err != nil {
		return nil, err
	}

	repo := &InMemoryRepository{
		storage: make(storage, cap),
	}

	for _, r := range rows {
		repo.storage[r.Sku] = &entity.Stock{
			Item: entity.OrderItem{
				ID:    r.Sku,
				Count: r.TotalCount,
			},
			Reserved: r.Reserved,
		}
	}

	return repo, nil
}

func (r *InMemoryRepository) StockReserve(_ context.Context, stock *entity.Stock) error {
	r.mx.Lock()
	defer r.mx.Unlock()

	if _, ok := r.storage[stock.Item.ID]; !ok {
		return model.ErrNotEnoughStocks
	}

	if r.storage[stock.Item.ID].Item.Count < stock.Reserved {
		return model.ErrNotEnoughStocks
	}

	r.storage[stock.Item.ID].Item.Count -= stock.Reserved
	r.storage[stock.Item.ID].Reserved += stock.Reserved

	return nil
}

func (r *InMemoryRepository) StockReserveRemove(_ context.Context, stock *entity.Stock) error {
	r.mx.Lock()
	defer r.mx.Unlock()

	if _, ok := r.storage[stock.Item.ID]; !ok {
		return model.ErrUnknownStock
	}

	if r.storage[stock.Item.ID].Reserved < stock.Reserved {
		return model.ErrNotEnoughStocks
	}

	r.storage[stock.Item.ID].Reserved -= stock.Reserved

	return nil
}

func (r *InMemoryRepository) StockCancel(_ context.Context, stock *entity.Stock) error {
	r.mx.Lock()
	defer r.mx.Unlock()

	if _, ok := r.storage[stock.Item.ID]; !ok {
		return model.ErrUnknownStock
	}

	if r.storage[stock.Item.ID].Reserved < stock.Reserved {
		return model.ErrNotEnoughStocks
	}

	r.storage[stock.Item.ID].Reserved -= stock.Reserved
	r.storage[stock.Item.ID].Item.Count += stock.Reserved

	return nil
}

func (r *InMemoryRepository) StockGetByID(_ context.Context, sku entity.Sku) (*entity.Stock, error) {
	r.mx.Lock()
	defer r.mx.Unlock()

	stock, ok := r.storage[sku]
	if !ok {
		return nil, model.ErrUnknownStock
	}

	return stock, nil
}
