package models

// Report structures
type ReportDefinition struct {
	ID           string
	Name         string
	SourceObject string
	// SourceRef is the neutral report source token. For single-object reports it
	// may match SourceObject; product dialects may use it for app-owned report
	// type identifiers such as Salesforce reportType.
	SourceRef string
	// DisplayMode is the neutral result presentation mode used by report
	// adapters. The executor still chooses grouped vs detail execution from the
	// grouping shape, not from product wire terms.
	DisplayMode string
	// FilterLogic is an optional app/dialect-owned expression that combines
	// Filters. The default L0 dialect leaves it uninterpreted.
	FilterLogic string
	DateFilter  *ReportDateFilter

	// Scope is the "Show me" filter: "all" (no filter), "mine" (OwnerId = current user),
	// or "myTeam" (OwnerId in current user + role subordinates). Persisted in the
	// Report.Scope column. Case-insensitive on read; empty means "all".
	Scope      string
	Columns    []string
	Filters    []ReportFilter
	Grouping   []string
	Aggregates []ReportAggregate
	// DegradeOnError controls whether the query retry logic may silently
	// drop SelectFields/Criteria on failure. When false (the default),
	// query errors are returned immediately. Dashboard components set
	// this to true so individual report failures don't break the whole
	// dashboard.
	DegradeOnError bool
}

// ReportDateFilter represents a neutral date range filter for a report.
type ReportDateFilter struct {
	Column        string
	Preset        string
	DurationValue string
	StartDate     string
	EndDate       string
}

func (d *ReportDefinition) NormalizeBoundaryAliases() {
	if d == nil {
		return
	}
	normalizeDateFilterAliases(d.DateFilter)
}

func (d *ReportDefinition) EffectiveSourceRef() string {
	if d == nil {
		return ""
	}
	if d.SourceRef != "" {
		return d.SourceRef
	}
	return ""
}

func (d *ReportDefinition) EffectiveDisplayMode() string {
	if d == nil {
		return ""
	}
	if d.DisplayMode != "" {
		return d.DisplayMode
	}
	return ""
}

func (d *ReportDefinition) EffectiveFilterLogic() string {
	if d == nil {
		return ""
	}
	return d.FilterLogic
}

func (d *ReportDefinition) EffectiveDateFilter() *ReportDateFilter {
	if d == nil {
		return nil
	}
	if d.DateFilter != nil {
		normalizeDateFilterAliases(d.DateFilter)
		return d.DateFilter
	}
	return nil
}

func normalizeDateFilterAliases(f *ReportDateFilter) {
	if f == nil {
		return
	}
	if f.Preset == "" {
		f.Preset = f.DurationValue
	}
	if f.DurationValue == "" {
		f.DurationValue = f.Preset
	}
}

type ReportFilter struct {
	Column   string
	Operator string
	Value    string
}

type ReportAggregate struct {
	Field string
	Type  string // SUM, AVG, COUNT, etc.
}

type ReportResult struct {
	ReportMetadata ReportDefinition
	FactMap        map[string]interface{} // For summary/matrix (keys -> aggregates)
	Rows           []SObject              // Tabular data or detail rows
	// TotalCount is the unpaginated row count from a separate COUNT(*) query.
	// Zero means "not computed" — the REST layer falls back to len(Rows).
	// Needed because Rows is capped by the execution limit (default 2000) and
	// the SF-compatible factMap.aggregates[RowCount] is supposed to reflect
	// the true total, not the display cap.
	TotalCount int
	// AggregateDedupColumn tells the handler's aggregate computation which
	// row key to dedupe on when summing a given aggregate field. Salesforce
	// computes SUM/AVG/MIN/MAX over primary-entity fields by deduping the
	// JOIN fan-out against the primary entity's Id; without that dedup the
	// same opportunity gets summed once per converted lead. Key = canonical
	// aggregate field name (e.g. "AMOUNT"); value = SObject row key that
	// holds the dedup id (e.g. "__primary_id__"). Absent entries mean the
	// aggregate is summed across raw rows (joined-entity fields, counts).
	AggregateDedupColumn map[string]string
	// Warnings carries structured degradation notices produced by the CRT
	// resolver/synthesizer (design §4.11). The REST layer copies these into
	// reportExtendedMetadata.warnings on the wire. Nil or empty means no
	// degradation occurred.
	Warnings []CRTWarning
}

// CRTWarningCode is the closed set of structured warning codes emitted when
// a Custom Report Type is degraded rather than fully resolved. Lives in the
// models package so ReportResult can reference it without importing the
// analytics feature package (which would be an import cycle).
type CRTWarningCode string

// CRTWarning is the structured degradation notice emitted by the CRT
// synthesizer/resolver. See docs/design/crt-report-support.md §4.11.
type CRTWarning struct {
	Code         CRTWarningCode `json:"code"`
	Message      string         `json:"message"`
	SourceObject string         `json:"sourceObject,omitempty"`
}
