// Package datasource defines the DataSource interface and shared types used by
// all data source implementations (EPICS, synthetic, stub, …).
package datasource

import (
	"context"
	"errors"
	"time"
)

// Quality indicates the reliability of a signal value.
type Quality uint8

const (
	QualityGood      Quality = iota // value is valid
	QualityUncertain                // value may be stale or approximate
	QualityBad                      // value is invalid
)

func (q Quality) String() string {
	switch q {
	case QualityGood:
		return "good"
	case QualityUncertain:
		return "uncertain"
	default:
		return "bad"
	}
}

// DataType describes the Go type of a signal's value.
type DataType uint8

const (
	TypeFloat64      DataType = iota // scalar float; Data is float64
	TypeFloat64Array                 // waveform; Data is []float64
	TypeString                       // Data is string
	TypeInt64                        // Data is int64
	TypeBool                         // Data is bool
	TypeEnum                         // Data is int64; see Metadata.EnumStrings
)

// Value is a timestamped signal reading.
type Value struct {
	Timestamp  time.Time
	Data       any // float64 | []float64 | string | int64 | bool
	Quality    Quality
	Severity   int  // raw EPICS alarm severity (0=NO_ALARM,1=MINOR,2=MAJOR,3=INVALID); 0 for sources without alarm info
	Status     int  // raw EPICS alarm status (.STAT); 0 for sources without alarm info
	MetaUpdate bool // if true, metadata was refreshed — dispatcher should re-send meta
}

// Metadata describes the static properties of a signal.
type Metadata struct {
	Name        string
	Type        DataType
	Unit        string
	Description string
	DisplayLow  float64
	DisplayHigh float64
	DriveLow    float64
	DriveHigh   float64
	EnumStrings []string // non-nil only for TypeEnum
	Writable    bool
	Properties  map[string]string // Channel Finder properties
	Tags        []string          // Channel Finder tags
}

// CancelFunc cancels a subscription started by DataSource.Subscribe.
type CancelFunc func()

// DataSource is implemented by every data source plugin.
type DataSource interface {
	// Name returns the unique short identifier for this source (e.g. "epics").
	Name() string

	// Connect initialises the connection to the underlying system.
	// It should return quickly; actual channel connections are lazy.
	Connect(ctx context.Context) error

	// ListSignals returns metadata for all signals known to this source.
	ListSignals(ctx context.Context) ([]Metadata, error)

	// GetMetadata returns the metadata for a single named signal.
	GetMetadata(ctx context.Context, signal string) (Metadata, error)

	// Subscribe registers ch to receive value updates for the named signal.
	// The first value is delivered as soon as it is available.
	// The returned CancelFunc must be called to release resources.
	Subscribe(ctx context.Context, signal string, ch chan<- Value) (CancelFunc, error)

	// Write sets a new value for a writable signal.
	Write(ctx context.Context, signal string, value any) error

	// History returns up to maxPoints values in [start, end].
	// Returns ErrHistoryUnavailable if the source has no archive.
	History(ctx context.Context, signal string, start, end time.Time, maxPoints int) ([]Value, error)
}

var (
	// ErrNotFound is returned when a signal does not exist in the source.
	ErrNotFound = errors.New("signal not found")

	// ErrNotWritable is returned when attempting to write to a read-only signal.
	ErrNotWritable = errors.New("signal is not writable")

	// ErrHistoryUnavailable is returned when the source has no archive backend.
	ErrHistoryUnavailable = errors.New("history unavailable for this data source")
)
