package domain

import (
	"context"
	"time"

	"github.com/google/uuid"
	"github.com/lib/pq"
	"gorm.io/gorm"
)

const (
	RPUsageOutboxStatusPending    = "pending"
	RPUsageOutboxStatusProcessing = "processing"
	RPUsageOutboxStatusProcessed  = "processed"
	RPUsageOutboxStatusFailed     = "failed"
)

const (
	RPUsageEventTypeAuthorizationGranted = "rp_usage.authorization_granted"
	RPUsageEventTypeAuthorizationRevoked = "rp_usage.authorization_revoked"
)

const (
	RPUsageTenantTypeCompany      = TenantTypeCompany
	RPUsageTenantTypeOrganization = TenantTypeOrganization
)

type RPUsageEvent struct {
	ID            string         `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
	EventType     string         `gorm:"not null;index:idx_rp_usage_outbox_event" json:"eventType"`
	Subject       string         `gorm:"not null;index:idx_rp_usage_outbox_subject" json:"subject"`
	TenantID      string         `gorm:"index:idx_rp_usage_outbox_tenant" json:"tenantId,omitempty"`
	TenantType    string         `gorm:"index:idx_rp_usage_outbox_tenant" json:"tenantType,omitempty"`
	ClientID      string         `gorm:"not null;index:idx_rp_usage_outbox_client" json:"clientId"`
	ClientName    string         `json:"clientName,omitempty"`
	SessionID     string         `gorm:"index" json:"sessionId,omitempty"`
	Scopes        pq.StringArray `gorm:"type:text[]" json:"scopes,omitempty"`
	Source        string         `gorm:"not null;index" json:"source"`
	CorrelationID string         `gorm:"index" json:"correlationId,omitempty"`
	Payload       JSONMap        `gorm:"type:jsonb" json:"payload,omitempty"`
	DedupeKey     string         `gorm:"uniqueIndex" json:"dedupeKey"`
	Status        string         `gorm:"default:'pending';index" json:"status"`
	RetryCount    int            `gorm:"default:0" json:"retryCount"`
	LastError     string         `json:"lastError,omitempty"`
	NextAttemptAt *time.Time     `json:"nextAttemptAt,omitempty"`
	OccurredAt    time.Time      `gorm:"not null;index" json:"occurredAt"`
	ProcessedAt   *time.Time     `json:"processedAt,omitempty"`
	CreatedAt     time.Time      `json:"createdAt"`
	UpdatedAt     time.Time      `json:"updatedAt"`
}

func (e *RPUsageEvent) TableName() string {
	return "rp_usage_outbox"
}

func (e *RPUsageEvent) BeforeCreate(tx *gorm.DB) error {
	if e.ID == "" {
		e.ID = uuid.NewString()
	}
	if e.Status == "" {
		e.Status = RPUsageOutboxStatusPending
	}
	if e.OccurredAt.IsZero() {
		e.OccurredAt = time.Now()
	}
	if e.Payload == nil {
		e.Payload = JSONMap{}
	}
	return nil
}

type RPUsageEventSink interface {
	EmitRPUsageEvent(ctx context.Context, event RPUsageEvent) error
}

type RPUsageProjectionRepository interface {
	CreateRPUsageEvent(ctx context.Context, event RPUsageEvent) error
}

type RPUsageDailyMetric struct {
	Date           string `json:"date"`
	TenantID       string `json:"tenantId"`
	TenantType     string `json:"tenantType"`
	TenantName     string `json:"tenantName,omitempty"`
	ClientID       string `json:"clientId"`
	ClientName     string `json:"clientName"`
	LoginRequests  uint64 `json:"loginRequests"`
	OtherRequests  uint64 `json:"otherRequests"`
	UniqueSubjects uint64 `json:"uniqueSubjects"`
}

type RPUsageQuery struct {
	Days     int
	Period   string
	TenantID string
}

type RPUsageQueryRepository interface {
	FindRPUsage(ctx context.Context, query RPUsageQuery) ([]RPUsageDailyMetric, error)
}
