package query

import (
	"fmt"
	"strings"
)

// Projection kinds for scope dive-path fields (Athena partition projection / S3 layout).
const (
	ProjectionEnum     = "enum"
	ProjectionDate     = "date"
	ProjectionInteger  = "integer"
	ProjectionInjected = "injected"
)

// ScopeFieldMeta describes one ordered dive-path scope field for API clients.
// Order in []ScopeFieldMeta is the Hive path order (parent → child).
type ScopeFieldMeta struct {
	Name       string `json:"name"`
	Projection string `json:"projection"`
	Required   bool   `json:"required"`
	PathKey    string `json:"path_key,omitempty"`  // Hive directory key (e.g. deployType); empty when BarePath
	BarePath   bool   `json:"bare_path,omitempty"` // true: S3 segment is the raw value (no key=)
}

// divePathField is the internal ordered partition level used for rewrite + S3 list.
type divePathField struct {
	Name       string // canonical API key
	APIKeys    []string
	PathKey    string // S3 / Hive path segment key (ignored when BarePath)
	SQLColumn  string
	Projection string
	Required   bool
	Normalize  func(string) (string, error)
	// PathSuffix is appended after this field's hive segment when building S3 list
	// prefixes (literal segments such as "type=topsql" that are not scope fields).
	PathSuffix string
	// BarePath: S3 directories are raw values (e.g. deltalake/{org}/{cluster}/slowlogs)
	// instead of Hive key=value segments.
	BarePath bool
}

func logsDivePath() []divePathField {
	return []divePathField{
		{Name: "env", APIKeys: []string{"env"}, PathKey: "env", SQLColumn: "env", Projection: ProjectionEnum, Required: true},
		{Name: "deploy_type", APIKeys: []string{"deploy_type", "deploytype"}, PathKey: "deployType", SQLColumn: "deploytype", Projection: ProjectionEnum, Required: true, Normalize: normalizeDeployTypeValue},
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionDate, Required: true, Normalize: normalizeLogsDate},
		{Name: "components", APIKeys: []string{"components", "component"}, PathKey: "component", SQLColumn: "component", Projection: ProjectionInjected, Required: true},
	}
}

func logStatsDivePath() []divePathField {
	return []divePathField{
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionInteger, Required: true, Normalize: normalizeLogStatsDate},
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true},
	}
}

// topsqlDivePath: deltalake/org=/cluster=/type=topsql/component=/instance=/date=/
func topsqlDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, PathSuffix: "type=topsql"},
		{Name: "component", APIKeys: []string{"component", "components"}, PathKey: "component", SQLColumn: "component", Projection: ProjectionInjected, Required: true},
		{Name: "instance", APIKeys: []string{"instance"}, PathKey: "instance", SQLColumn: "instance", Projection: ProjectionInjected, Required: true},
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionDate, Required: true, Normalize: normalizeLogsDate},
	}
}

// sqlstatementDivePath: deltalake/org=/cluster=/type=sqlstatement/component=sqlstatement/date=/
func sqlstatementDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, PathSuffix: "type=sqlstatement/component=sqlstatement"},
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionDate, Required: true, Normalize: normalizeLogsDate},
	}
}

// slowlogsDivePath: legacy Delta root deltalake/{org}/{cluster}/slowlogs/date={yyyyMMdd}/
// (bare tenant / o11y-cluster id segments; date is a Hive path partition).
func slowlogsDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org", "tenant", "tenant_id"}, PathKey: "", SQLColumn: "org", Projection: ProjectionInjected, Required: true, BarePath: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, BarePath: true, PathSuffix: "slowlogs"},
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionInteger, Required: true, Normalize: normalizeLogStatsDate},
	}
}

// topruDivePath: deltalake/org=/cluster=/type=topsql/component=topru/instance=default/date=/
// (matches o11ydiagnosis/pkg/topru topRUTablePath + date partition used by topsql_data_deltalake).
func topruDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, PathSuffix: "type=topsql/component=topru/instance=default"},
		{Name: "date", APIKeys: []string{"date"}, PathKey: "date", SQLColumn: "date", Projection: ProjectionDate, Required: true, Normalize: normalizeLogsDate},
	}
}

// topsqlSQLMetaDivePath: deltalake/org=/cluster=/type=topsql/component=topsql_sql_meta (no instance).
func topsqlSQLMetaDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, PathSuffix: "type=topsql/component=topsql_sql_meta"},
	}
}

// topsqlPlanMetaDivePath: deltalake/org=/cluster=/type=topsql/component=topsql_plan_meta (no instance).
func topsqlPlanMetaDivePath() []divePathField {
	return []divePathField{
		{Name: "org", APIKeys: []string{"org"}, PathKey: "org", SQLColumn: "org", Projection: ProjectionInjected, Required: true},
		{Name: "cluster", APIKeys: []string{"cluster"}, PathKey: "cluster", SQLColumn: "cluster", Projection: ProjectionInjected, Required: true, PathSuffix: "type=topsql/component=topsql_plan_meta"},
	}
}

func divePathForDataset(ds string) ([]divePathField, error) {
	switch normalizeDataset(ds) {
	case DatasetLogs:
		return logsDivePath(), nil
	case DatasetLogStats:
		return logStatsDivePath(), nil
	case DatasetTopsql:
		return topsqlDivePath(), nil
	case DatasetTopsqlSQLMeta:
		return topsqlSQLMetaDivePath(), nil
	case DatasetTopsqlPlanMeta:
		return topsqlPlanMetaDivePath(), nil
	case DatasetTopru:
		return topruDivePath(), nil
	case DatasetSQLStatement:
		return sqlstatementDivePath(), nil
	case DatasetSlowlogs:
		return slowlogsDivePath(), nil
	case DatasetMetricsStatusSources, DatasetMetricsStatusByMetric,
		DatasetMetricsStatusByLabel, DatasetMetricsStatusByLabelValue:
		return metricsStatusDivePath(), nil
	default:
		return nil, fmt.Errorf("dataset %q does not support scope listing", ds)
	}
}

func scopeFieldMetas(fields []divePathField) []ScopeFieldMeta {
	out := make([]ScopeFieldMeta, 0, len(fields))
	for _, f := range fields {
		out = append(out, ScopeFieldMeta{
			Name:       f.Name,
			Projection: f.Projection,
			Required:   f.Required,
			PathKey:    f.PathKey,
			BarePath:   f.BarePath,
		})
	}
	return out
}

func pathListSegment(f divePathField, value string) string {
	if f.BarePath || strings.TrimSpace(f.PathKey) == "" {
		return value
	}
	return hivePathSegment(f.PathKey, value)
}

func findDiveField(fields []divePathField, name string) (divePathField, int, bool) {
	name = strings.ToLower(strings.TrimSpace(name))
	for i, f := range fields {
		for _, k := range f.APIKeys {
			if strings.EqualFold(k, name) {
				return f, i, true
			}
		}
	}
	return divePathField{}, -1, false
}

func (f divePathField) resolveEquality(scope Scope) (string, error) {
	v, key, ok := scope.lookup(f.APIKeys...)
	if !ok {
		return "", fmt.Errorf("scope.%s is required", f.Name)
	}
	if v.eq == nil {
		return "", fmt.Errorf("scope.%s must be a string equality value for path listing (arrays/ranges not supported as ancestors)", key)
	}
	raw := strings.TrimSpace(*v.eq)
	if raw == "" {
		return "", fmt.Errorf("scope.%s must not be empty", f.Name)
	}
	if f.Normalize != nil {
		return f.Normalize(raw)
	}
	return raw, nil
}

func hivePathSegment(pathKey, value string) string {
	return pathKey + "=" + value
}
