package evidence

import (
	"context"
	"errors"
	"strings"

	platformregistry "github.com/gpuaas/platform/packages/platform/registry"
)

var (
	ErrInvalidInput   = errors.New("invalid evidence input")
	ErrBundleNotFound = errors.New("evidence bundle not found")
)

type ListBundlesFilter struct {
	EnvironmentProfile string
	ProductScope       string
	GateState          Result
	Cursor             string
	PageSize           int
}

type Store interface {
	ListBundles(ctx context.Context, filter ListBundlesFilter) ([]BundleSummary, *string, error)
	GetBundle(ctx context.Context, bundleID string) (*Bundle, error)
	CreateBundle(ctx context.Context, input CreateBundleInput) (*Bundle, error)
	RecordItem(ctx context.Context, input RecordItemInput) (*Item, error)
	UpsertInvariantCoverage(ctx context.Context, input RecordInvariantInput) (*ProductInvariantCoverage, error)
}

type Service struct {
	store    Store
	registry EvidenceTypeRegistry
}

func NewService(store Store) *Service {
	return &Service{store: store}
}

type EvidenceTypeRegistry interface {
	GetEvidenceType(ctx context.Context, evidenceType string) (*platformregistry.EvidenceType, error)
}

func NewServiceWithRegistry(store Store, registry EvidenceTypeRegistry) *Service {
	return &Service{store: store, registry: registry}
}

func (s *Service) ListBundles(ctx context.Context, filter ListBundlesFilter) ([]BundleSummary, *string, error) {
	if filter.GateState != "" && !validResult(filter.GateState) {
		return nil, nil, ErrInvalidInput
	}
	if s == nil || s.store == nil {
		return nil, nil, ErrBundleNotFound
	}
	return s.store.ListBundles(ctx, filter)
}

func (s *Service) GetBundle(ctx context.Context, bundleID string) (*Bundle, error) {
	if strings.TrimSpace(bundleID) == "" {
		return nil, ErrInvalidInput
	}
	if s == nil || s.store == nil {
		return nil, ErrBundleNotFound
	}
	return s.store.GetBundle(ctx, strings.TrimSpace(bundleID))
}

func (s *Service) CreateBundle(ctx context.Context, input CreateBundleInput) (*Bundle, error) {
	if strings.TrimSpace(input.SourceCommit) == "" ||
		strings.TrimSpace(input.EnvironmentProfile) == "" ||
		strings.TrimSpace(input.ProductScope) == "" ||
		strings.TrimSpace(input.ChangeSummary) == "" {
		return nil, ErrInvalidInput
	}
	if s == nil || s.store == nil {
		return nil, ErrBundleNotFound
	}
	input.SourceCommit = strings.TrimSpace(input.SourceCommit)
	input.EnvironmentProfile = strings.TrimSpace(input.EnvironmentProfile)
	input.ProductScope = strings.TrimSpace(input.ProductScope)
	input.ChangeSummary = strings.TrimSpace(input.ChangeSummary)
	return s.store.CreateBundle(ctx, input)
}

func (s *Service) RecordItem(ctx context.Context, input RecordItemInput) (*Item, error) {
	if strings.TrimSpace(input.BundleID) == "" ||
		strings.TrimSpace(input.EvidenceType) == "" ||
		strings.TrimSpace(input.Producer) == "" ||
		strings.TrimSpace(input.Owner) == "" ||
		strings.TrimSpace(input.RetentionClass) == "" ||
		!validResult(input.Result) {
		return nil, ErrInvalidInput
	}
	if s == nil || s.store == nil {
		return nil, ErrBundleNotFound
	}
	input.BundleID = strings.TrimSpace(input.BundleID)
	input.EvidenceType = strings.TrimSpace(input.EvidenceType)
	input.Producer = strings.TrimSpace(input.Producer)
	input.Owner = strings.TrimSpace(input.Owner)
	input.RetentionClass = strings.TrimSpace(input.RetentionClass)
	if err := s.validateEvidenceType(ctx, input); err != nil {
		return nil, err
	}
	return s.store.RecordItem(ctx, input)
}

func (s *Service) RecordInvariantCoverage(ctx context.Context, input RecordInvariantInput) (*ProductInvariantCoverage, error) {
	if strings.TrimSpace(input.BundleID) == "" ||
		strings.TrimSpace(input.InvariantID) == "" ||
		strings.TrimSpace(input.ProductArea) == "" ||
		!validResult(input.Result) {
		return nil, ErrInvalidInput
	}
	if s == nil || s.store == nil {
		return nil, ErrBundleNotFound
	}
	input.BundleID = strings.TrimSpace(input.BundleID)
	input.InvariantID = strings.TrimSpace(input.InvariantID)
	input.ProductArea = strings.TrimSpace(input.ProductArea)
	return s.store.UpsertInvariantCoverage(ctx, input)
}

func (s *Service) validateEvidenceType(ctx context.Context, input RecordItemInput) error {
	if s == nil || s.registry == nil {
		return nil
	}
	entry, err := s.registry.GetEvidenceType(ctx, input.EvidenceType)
	if err != nil || entry == nil {
		return ErrInvalidInput
	}
	if entry.Lifecycle != platformregistry.LifecycleActive {
		return ErrInvalidInput
	}
	if strings.TrimSpace(entry.RetentionClass) != "" && strings.TrimSpace(input.RetentionClass) != strings.TrimSpace(entry.RetentionClass) {
		return ErrInvalidInput
	}
	if len(entry.AllowedResults) > 0 && !registryAllowsResult(entry.AllowedResults, input.Result) {
		return ErrInvalidInput
	}
	return nil
}

func registryAllowsResult(allowed []platformregistry.Result, result Result) bool {
	for _, candidate := range allowed {
		if string(candidate) == string(result) {
			return true
		}
	}
	return false
}

func validResult(result Result) bool {
	switch result {
	case ResultPass, ResultFail, ResultPartial, ResultBlocked, ResultMissing, ResultNotApplicable:
		return true
	default:
		return false
	}
}
