package validator

import (
	"errors"
	"strings"
	"time"

	"apskel-pos-be/internal/constants"
	"apskel-pos-be/internal/contract"
)

type PurchaseOrderValidator interface {
	ValidateCreatePurchaseOrderRequest(req *contract.CreatePurchaseOrderRequest) (error, string)
	ValidateUpdatePurchaseOrderRequest(req *contract.UpdatePurchaseOrderRequest) (error, string)
	ValidateListPurchaseOrdersRequest(req *contract.ListPurchaseOrdersRequest) (error, string)
}

type PurchaseOrderValidatorImpl struct{}

func NewPurchaseOrderValidator() *PurchaseOrderValidatorImpl {
	return &PurchaseOrderValidatorImpl{}
}

func (v *PurchaseOrderValidatorImpl) ValidateCreatePurchaseOrderRequest(req *contract.CreatePurchaseOrderRequest) (error, string) {
	if req == nil {
		return errors.New("request body is required"), constants.MissingFieldErrorCode
	}

	if req.VendorID.String() == "" {
		return errors.New("vendor_id is required"), constants.MissingFieldErrorCode
	}

	if strings.TrimSpace(req.PONumber) == "" {
		return errors.New("po_number is required"), constants.MissingFieldErrorCode
	}

	if len(req.PONumber) < 1 || len(req.PONumber) > 50 {
		return errors.New("po_number must be between 1 and 50 characters"), constants.MalformedFieldErrorCode
	}

	// Validate transaction date
	if strings.TrimSpace(req.TransactionDate) == "" {
		return errors.New("transaction_date is required"), constants.MissingFieldErrorCode
	}
	transactionDate, err := time.Parse("2006-01-02", req.TransactionDate)
	if err != nil {
		return errors.New("transaction_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
	}

	// Validate due date
	if strings.TrimSpace(req.DueDate) == "" {
		return errors.New("due_date is required"), constants.MissingFieldErrorCode
	}
	dueDate, err := time.Parse("2006-01-02", req.DueDate)
	if err != nil {
		return errors.New("due_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
	}

	// Check if due date is after transaction date
	if dueDate.Before(transactionDate) {
		return errors.New("due_date must be after transaction_date"), constants.MalformedFieldErrorCode
	}

	if req.Reference != nil && len(*req.Reference) > 100 {
		return errors.New("reference must be at most 100 characters"), constants.MalformedFieldErrorCode
	}

	if req.Status != nil {
		validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
		if !contains(validStatuses, *req.Status) {
			return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
		}
	}

	if len(req.Items) == 0 {
		return errors.New("at least one item is required"), constants.MissingFieldErrorCode
	}

	// Validate items
	for i, item := range req.Items {
		if err, code := v.validatePurchaseOrderItem(&item, i); err != nil {
			return err, code
		}
	}

	return nil, ""
}

func (v *PurchaseOrderValidatorImpl) ValidateUpdatePurchaseOrderRequest(req *contract.UpdatePurchaseOrderRequest) (error, string) {
	if req == nil {
		return errors.New("request body is required"), constants.MissingFieldErrorCode
	}

	if req.PONumber != nil {
		if strings.TrimSpace(*req.PONumber) == "" {
			return errors.New("po_number cannot be empty"), constants.MalformedFieldErrorCode
		}
		if len(*req.PONumber) < 1 || len(*req.PONumber) > 50 {
			return errors.New("po_number must be between 1 and 50 characters"), constants.MalformedFieldErrorCode
		}
	}

	// Validate dates if both are provided
	if req.TransactionDate != nil && req.DueDate != nil {
		if *req.TransactionDate != "" && *req.DueDate != "" {
			transactionDate, err := time.Parse("2006-01-02", *req.TransactionDate)
			if err != nil {
				return errors.New("transaction_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
			}
			
			dueDate, err := time.Parse("2006-01-02", *req.DueDate)
			if err != nil {
				return errors.New("due_date must be in YYYY-MM-DD format"), constants.MalformedFieldErrorCode
			}
			
			if dueDate.Before(transactionDate) {
				return errors.New("due_date must be after transaction_date"), constants.MalformedFieldErrorCode
			}
		}
	}

	if req.Reference != nil && len(*req.Reference) > 100 {
		return errors.New("reference must be at most 100 characters"), constants.MalformedFieldErrorCode
	}

	if req.Status != nil {
		validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
		if !contains(validStatuses, *req.Status) {
			return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
		}
	}

	// Validate items if provided
	if req.Items != nil {
		for i, item := range req.Items {
			if err, code := v.validateUpdatePurchaseOrderItem(&item, i); err != nil {
				return err, code
			}
		}
	}

	return nil, ""
}

func (v *PurchaseOrderValidatorImpl) ValidateListPurchaseOrdersRequest(req *contract.ListPurchaseOrdersRequest) (error, string) {
	if req == nil {
		return errors.New("request body is required"), constants.MissingFieldErrorCode
	}

	if req.Page < 1 {
		return errors.New("page must be at least 1"), constants.MalformedFieldErrorCode
	}

	if req.Limit < 1 || req.Limit > 100 {
		return errors.New("limit must be between 1 and 100"), constants.MalformedFieldErrorCode
	}

	if req.Status != "" {
		validStatuses := []string{"draft", "sent", "approved", "received", "cancelled"}
		if !contains(validStatuses, req.Status) {
			return errors.New("status must be one of: draft, sent, approved, received, cancelled"), constants.MalformedFieldErrorCode
		}
	}

	if req.StartDate != nil && req.EndDate != nil {
		if req.EndDate.Before(*req.StartDate) {
			return errors.New("end_date must be after start_date"), constants.MalformedFieldErrorCode
		}
	}

	return nil, ""
}

func (v *PurchaseOrderValidatorImpl) validatePurchaseOrderItem(item *contract.CreatePurchaseOrderItemRequest, index int) (error, string) {
	if item.IngredientID.String() == "" {
		return errors.New("items[" + string(rune(index)) + "].ingredient_id is required"), constants.MissingFieldErrorCode
	}

	if item.Quantity <= 0 {
		return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
	}

	if item.UnitID.String() == "" {
		return errors.New("items[" + string(rune(index)) + "].unit_id is required"), constants.MissingFieldErrorCode
	}

	if item.Amount < 0 {
		return errors.New("items[" + string(rune(index)) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
	}

	return nil, ""
}

func (v *PurchaseOrderValidatorImpl) validateUpdatePurchaseOrderItem(item *contract.UpdatePurchaseOrderItemRequest, index int) (error, string) {
	if item.Quantity != nil && *item.Quantity <= 0 {
		return errors.New("items[" + string(rune(index)) + "].quantity must be greater than 0"), constants.MalformedFieldErrorCode
	}

	if item.Amount != nil && *item.Amount < 0 {
		return errors.New("items[" + string(rune(index)) + "].amount must be greater than or equal to 0"), constants.MalformedFieldErrorCode
	}

	return nil, ""
}

// Helper function to check if a string is in a slice
func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}
