package service

import (
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"golang.org/x/crypto/pbkdf2"
	"log"
	"rrbec_server/internal/models"
	"rrbec_server/internal/repository"
	"strings"
	"time"
)

type Service struct {
	repo *repository.Repository
}

func NewService(repo *repository.Repository) *Service {
	return &Service{repo: repo}
}

// Comanda Logic
func (s *Service) CreateComanda(comanda *models.Comanda) error {
	comanda.DtOpen = time.Now()
	comanda.Status = "OPEN"
	return s.repo.CreateComanda(comanda)
}

// Item Addition and Stock Logic
func (s *Service) AddItemToComanda(comandaID, productID uint, applicant string) (*models.ProductComanda, error) {
	comanda, err := s.repo.GetComandaByID(comandaID)
	if err != nil {
		return nil, err
	}
	if comanda.Status != "OPEN" {
		return nil, errors.New("cannot add items to a closed or fiado comanda")
	}

	product, err := s.repo.GetProductByID(productID)
	if err != nil {
		return nil, errors.New("product not found")
	}

	if product.Quantity <= 0 {
		return nil, errors.New("product out of stock")
	}

	item := &models.ProductComanda{
		ComandaID:    comandaID,
		ProductID:    productID,
		ProductName:  product.Name,
		ProductPrice: product.Price,
		DateTime:     time.Now(),
		Applicant:    applicant,
	}
	err = s.repo.AddItemToComanda(item)
	if err != nil {
		return nil, err
	}

	if err := s.deductProductStock(productID, 1); err != nil {
		log.Printf("Warning: failed to deduct stock for product %d: %v", productID, err)
	}

	if product.Cuisine {
		order := &models.Order{
			ProductComandaID: &item.ID,
			ProductID:        productID,
			ComandaID:        comandaID,
			Queue:            time.Now(),
		}
		s.repo.CreateOrder(order)
	}

	return item, nil
}

func (s *Service) deductProductStock(productID uint, quantity int) error {
	components, err := s.repo.GetProductComponents(productID)
	if err != nil {
		return err
	}

	if len(components) > 0 {
		for _, comp := range components {
			if err := s.repo.DeductStock(comp.ComponentProductID, int(comp.QuantityRequired)); err != nil {
				return err
			}
		}
		return nil
	}

	return s.repo.DeductStock(productID, quantity)
}

func (s *Service) AddItemToComandaRaw(item *models.ProductComanda) error {
	comanda, err := s.repo.GetComandaByID(item.ComandaID)
	if err != nil {
		return err
	}
	if comanda.Status != "OPEN" {
		return errors.New("cannot add items to a closed or fiado comanda")
	}

	product, err := s.repo.GetProductByID(item.ProductID)
	if err != nil {
		return errors.New("product not found")
	}

	item.ProductName = product.Name
	item.ProductPrice = product.Price
	item.DateTime = time.Now()

	return s.repo.AddItemToComanda(item)
}

// Sync Logic Placeholder
func (s *Service) SyncWithCloud() error {
	// Task for the background worker
	return nil
}

func (s *Service) GetProducts(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetProducts(params)
}

func (s *Service) CreateProduct(product *models.Product) error {
	return s.repo.CreateProduct(product)
}

func (s *Service) UpdateProduct(id uint, updates map[string]interface{}) error {
	return s.repo.UpdateProductFields(id, updates)
}

func (s *Service) GetMesas(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetMesas(params)
}

func (s *Service) GetComandas(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetComandas(params)
}

func (s *Service) GetComandaByID(id uint) (*models.Comanda, error) {
	return s.repo.GetComandaByID(id)
}

func (s *Service) GetCategories(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetCategories(params)
}

func (s *Service) CreateCategory(cat *models.Category) error {
	return s.repo.CreateCategory(cat)
}

func (s *Service) UpdateCategory(id uint, updates map[string]interface{}) error {
	return s.repo.UpdateCategoryFields(id, updates)
}

func (s *Service) GetTypePayments(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetTypePayments(params)
}

func (s *Service) GetClients(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetClients(params)
}

func (s *Service) GetOrders(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetOrders(params)
}

func (s *Service) CreateOrder(order *models.Order) error {
	order.Queue = time.Now()
	return s.repo.CreateOrder(order)
}

func (s *Service) UpdateOrder(id uint, updates map[string]interface{}) error {
	return s.repo.UpdateOrderFields(id, updates)
}

func (s *Service) SetOrderPreparing(id uint) error {
	now := time.Now()
	return s.repo.UpdateOrderFields(id, map[string]interface{}{"preparing": &now})
}

func (s *Service) SetOrderFinished(id uint) error {
	now := time.Now()
	return s.repo.UpdateOrderFields(id, map[string]interface{}{"finished": &now})
}

func (s *Service) SetOrderDelivered(id uint) error {
	now := time.Now()
	return s.repo.UpdateOrderFields(id, map[string]interface{}{"delivered": &now})
}

func (s *Service) SetOrderCanceled(id uint) error {
	now := time.Now()
	return s.repo.UpdateOrderFields(id, map[string]interface{}{"canceled": &now})
}

func (s *Service) GetPayments(params *repository.PaginationParams) (*repository.PaginatedResponse, error) {
	return s.repo.GetPayments(params)
}

func (s *Service) DeleteItem(itemID uint) error {
	// 1. Check if there's an associated Order (Kitchen)
	order, err := s.repo.GetOrderByPC(itemID)
	if err == nil && order != nil {
		// 2. Mark as Canceled
		now := time.Now()
		order.Canceled = &now
		s.repo.UpdateOrder(order)
	}

	// 3. Delete the item
	return s.repo.DeleteItem(itemID)
}

func (s *Service) ClearComanda(id uint) error {
	if err := s.repo.ClearComandaItems(id); err != nil {
		return err
	}
	return s.repo.UpdateComandaStatus(id, "CLOSED")
}

func (s *Service) PagarComanda(id uint, payment *models.Payment, status string) error {
	payment.ComandaID = id
	payment.DateTime = time.Now()

	if err := s.repo.CreatePayment(payment); err != nil {
		return err
	}

	if status == "" {
		status = "CLOSED"
	}
	return s.repo.UpdateComandaStatus(id, status)
}

func (s *Service) UpdateComanda(id uint, updates map[string]interface{}) error {
	return s.repo.UpdateComandaFields(id, updates)
}

// User Auth
func (s *Service) Login(username, password string) (*models.User, error) {
	user, err := s.repo.GetUserByUsername(username)
	if err != nil {
		return nil, errors.New("invalid credentials")
	}

	if s.CheckDjangoPassword(password, user.Password) {
		return user, nil
	}

	return nil, errors.New("invalid credentials")
}

func (s *Service) CheckDjangoPassword(password, hash string) bool {
	parts := strings.Split(hash, "$")
	if len(parts) != 4 {
		return false
	}

	algorithm := parts[0]
	if algorithm != "pbkdf2_sha256" {
		return false
	}

	var iterations int
	fmt.Sscanf(parts[1], "%d", &iterations)
	salt := parts[2]
	djangoHash := parts[3]

	// PBKDF2 with SHA256
	dk := pbkdf2.Key([]byte(password), []byte(salt), iterations, 32, sha256.New)
	encoded := base64.StdEncoding.EncodeToString(dk)

	return encoded == djangoHash
}
