package db

import (
	"context"
	"database/sql"
	"time"

	"go.uber.org/zap/zapcore"
)

type EventType struct {
	ID           int    `json:"id" db:"id"`
	Name         string `json:"name" db:"name"`
	Description  string `json:"description" db:"description"`
	Schema       string `json:"schema" db:"schema"`
	RepoFilePath string `json:"repo_filepath" db:"repo_filepath"`
	Deprecated   bool   `json:"deprecated" db:"deprecated"`
	LDAPGroup    string `json:"ldap_group" db:"ldap_group"`
}

func (et EventType) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddInt("id", et.ID)
	enc.AddString("name", et.Name)
	enc.AddString("repoFilePath", et.RepoFilePath)
	enc.AddBool("deprecated", et.Deprecated)
	enc.AddString("ldapGroup", et.LDAPGroup)
	// omit schema and description because those are potentially very large,
	// and probably not useful in the context of logging
	return nil
}

type EventStream struct {
	ID          int               `json:"id" db:"id"`
	Environment string            `json:"environment" db:"environment"`
	SNSDetails  SNSDetails        `json:"sns_details" db:"sns_details"`
	EventTypeID int               `json:"event_type_id" db:"event_type_id"`
	AWSLeaseID  sql.NullInt64     `json:"aws_lease_id" db:"aws_lease_id"`
	EventType   `db:"event_type"` // For doing marshals from joins
}

type EventStreamEditable struct{} // nothing can be edited via API

// EventStreamInfraUpdate is used by processes making AWS infra changes
// to update record fields protected by a lease
type EventStreamInfraUpdate struct {
	SNSTopicARN string
}

type SNSDetails struct {
	SNSTopicARN string `json:"sns_topic_arn" db:"sns_topic_arn"`
}

type EventTypeEditable struct {
	Name         string
	Description  string
	Schema       string
	RepoFilePath string
	Deprecated   bool
	LDAPGroup    string
}

func (ete EventTypeEditable) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("name", ete.Name)
	enc.AddString("repoFilePath", ete.RepoFilePath)
	enc.AddBool("deprecated", ete.Deprecated)
	// omit schema and description because those are potentially very large,
	// and probably not useful in the context of logging
	return nil
}

type EventTypesDB interface {
	EventTypeCreate(ctx context.Context, eventType *EventType) (int, error)

	EventTypeUpdate(ctx context.Context, id int, eventTypeEditable *EventTypeEditable) (int, error)
	EventTypeDeleteByName(ctx context.Context, name string) error

	EventTypes(ctx context.Context) ([]*EventType, error)
	EventTypeByID(ctx context.Context, id int) (*EventType, error)
	EventTypeByName(ctx context.Context, name string) (*EventType, error)
}

type EventStreamsDB interface {
	EventStreamCreate(ctx context.Context, eventStream *EventStream) (int, error)

	EventStreamUpdate(ctx context.Context, id int, eventStreamEditable *EventStreamEditable) (int, error)
	EventStreamUpdateInfra(ctx context.Context, lease AWSLease, id int, eventStreamInfraUpdate *EventStreamInfraUpdate) (int, error)
	EventStreamDeleteByEventTypeID(ctx context.Context, eventTypeID int) (int, error)

	EventStreams(ctx context.Context) ([]*EventStream, error)
	EventStreamByID(ctx context.Context, id int) (*EventStream, error)
	EventStreamsByEnvironment(ctx context.Context, environment string) ([]*EventStream, error)
	EventStreamsByEventTypeID(ctx context.Context, eventTypeID int) ([]*EventStream, error)
	EventStreamsByName(ctx context.Context, eventName string) ([]*EventStream, error)
	EventStreamByNameAndEnvironment(ctx context.Context, name, environment string) (*EventStream, error)

	EventStreamAcquireLease(ctx context.Context, resourceID int, timeout time.Duration) (AWSLease, context.Context, error)
	EventStreamReleaseLease(lease AWSLease) error
}

func (es EventStream) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("eventType", es.EventType.Name)
	enc.AddString("environment", es.Environment)

	if es.AWSLeaseID.Valid {
		enc.AddInt64("lease", es.AWSLeaseID.Int64)
	} else {
		enc.AddString("lease", "nil")
	}

	enc.AddString("description", es.Description)
	enc.AddString("schema", es.Schema)
	enc.AddString("repoFilePath", es.RepoFilePath)
	enc.AddString("snsTopicARN", es.SNSDetails.SNSTopicARN)
	enc.AddBool("deprecated", es.EventType.Deprecated)

	return nil
}

func (ese EventStreamEditable) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	return nil
}

func (esiu EventStreamInfraUpdate) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("snsTopicARN", esiu.SNSTopicARN)
	return nil
}
