package order

import (
	"context"
	"errors"
)

var (
	ErrDependencyUnavailable   = errors.New("dependency unavailable")
	ErrInvalidRentHours        = errors.New("invalid rent hours")
	ErrListingUnavailable      = errors.New("listing unavailable")
	ErrCannotRentOwnListing    = errors.New("cannot rent own listing")
	ErrInsufficientBalance     = errors.New("insufficient balance")
	ErrOrderCannotPay          = errors.New("order cannot pay")
	ErrChannelPaymentRequired  = errors.New("channel payment required")
	ErrOrderCannotCancel       = errors.New("order cannot cancel")
	ErrOrderCannotHandoff      = errors.New("order cannot handoff")
	ErrOrderCannotResetHandoff = errors.New("order cannot reset handoff")
	ErrOrderCannotReceive      = errors.New("order cannot receive")
	ErrOrderCannotReturn       = errors.New("order cannot return")
	ErrOrderCannotComplete     = errors.New("order cannot complete")
	ErrCheckoutCannotSubmit    = errors.New("checkout cannot submit")
	ErrCheckoutCannotConfirm   = errors.New("checkout cannot confirm")
	ErrCheckoutCannotCounter   = errors.New("checkout cannot counter")
	ErrInvalidCheckoutAmount   = errors.New("invalid checkout amount")
	ErrPermissionDenied        = errors.New("permission denied")
)

const internalOrderHours = 24

type Service struct {
	repo *Repository
}

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

func (s *Service) Create(ctx context.Context, userID uint64, req CreateRequest) (*OrderDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if req.ListingID == 0 {
		return nil, ErrInvalidRentHours
	}
	return s.repo.Create(ctx, userID, req)
}

func (s *Service) Cancel(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.Cancel(ctx, userID, orderID)
}

func (s *Service) Pay(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	if orderID == 0 {
		return ErrOrderCannotPay
	}
	return s.repo.Pay(ctx, userID, orderID)
}

func (s *Service) SubmitHandoff(ctx context.Context, userID uint64, orderID uint64, req SubmitHandoffRequest) (*HandoffRecordDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 || req.Content == "" {
		return nil, ErrOrderCannotHandoff
	}
	return s.repo.SubmitHandoff(ctx, userID, orderID, req)
}

func (s *Service) ConfirmReceive(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.ConfirmReceive(ctx, userID, orderID)
}

func (s *Service) HandoffRecords(ctx context.Context, userID uint64, orderID uint64) ([]HandoffRecordDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.HandoffRecords(ctx, userID, orderID)
}

func (s *Service) SubmitReturn(ctx context.Context, userID uint64, orderID uint64, req SubmitReturnRequest) (*HandoffRecordDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 || req.Content == "" {
		return nil, ErrOrderCannotReturn
	}
	return s.repo.SubmitReturn(ctx, userID, orderID, req)
}

func (s *Service) SubmitCheckout(ctx context.Context, userID uint64, orderID uint64, req SubmitCheckoutRequest) (*HandoffRecordDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 || req.Content == "" {
		return nil, ErrCheckoutCannotSubmit
	}
	return s.repo.SubmitCheckout(ctx, userID, orderID, req)
}

func (s *Service) ConfirmReturn(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.ConfirmReturn(ctx, userID, orderID)
}

func (s *Service) ConfirmCheckout(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.ConfirmCheckout(ctx, userID, orderID)
}

func (s *Service) CounterCheckout(ctx context.Context, userID uint64, orderID uint64, req CounterCheckoutRequest) (*CheckoutDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 || req.Reason == "" {
		return nil, ErrCheckoutCannotCounter
	}
	return s.repo.CounterCheckout(ctx, userID, orderID, req)
}

func (s *Service) AcceptCheckout(ctx context.Context, userID uint64, orderID uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.AcceptCheckout(ctx, userID, orderID)
}

func (s *Service) ListForUser(ctx context.Context, userID uint64) ([]OrderDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.ListForUser(ctx, userID)
}

func (s *Service) ListAdmin(ctx context.Context, query AdminOrderQuery) (*PaginatedResult, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.ListAdmin(ctx, query)
}

func (s *Service) FindAdmin(ctx context.Context, orderID uint64) (*OrderDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.FindAdmin(ctx, orderID)
}

func (s *Service) FindLatestAdminByListing(ctx context.Context, listingID uint64) (*OrderDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if listingID == 0 {
		return nil, ErrListingUnavailable
	}
	return s.repo.FindLatestAdminByListing(ctx, listingID)
}

func (s *Service) HandoffRecordsAdmin(ctx context.Context, orderID uint64) ([]HandoffRecordDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.HandoffRecordsAdmin(ctx, orderID)
}

func (s *Service) AdminClose(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	if orderID == 0 || req.Reason == "" {
		return ErrOrderCannotComplete
	}
	return s.repo.AdminClose(ctx, adminID, orderID, req, meta)
}

func (s *Service) AdminMarkAbnormal(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	if orderID == 0 || req.Reason == "" {
		return ErrOrderCannotComplete
	}
	return s.repo.AdminMarkAbnormal(ctx, adminID, orderID, req, meta)
}

func (s *Service) AdminResetHandoff(ctx context.Context, adminID uint64, orderID uint64, req AdminActionRequest, meta AuditMeta) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	if orderID == 0 || req.Reason == "" {
		return ErrOrderCannotResetHandoff
	}
	return s.repo.AdminResetHandoff(ctx, adminID, orderID, req, meta)
}

func (s *Service) AdminRefund(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 {
		return nil, ErrOrderCannotComplete
	}
	return s.repo.AdminRefund(ctx, orderID)
}

func (s *Service) AdminRefundStatus(ctx context.Context, orderID uint64) (*RefundStatusDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if orderID == 0 {
		return nil, ErrOrderCannotComplete
	}
	return s.repo.AdminRefundStatus(ctx, orderID)
}

func (s *Service) FindForUser(ctx context.Context, userID uint64, orderID uint64) (*OrderDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.FindForUser(ctx, userID, orderID)
}
