package db

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

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

// ErrSubscriptionNotFound TODO: remove?
var ErrSubscriptionNotFound = errors.New("subscription not found")
var ErrSubscriptionTargetNotFound = errors.New("subscription target not found")

type SubscriptionTarget struct {
	ID                 int           `json:"id" db:"id"`
	Name               string        `json:"name" db:"name"`
	AssumeRoleARN      string        `json:"assume_role_arn" db:"assume_role_arn"`
	SQSDetails         SQSDetails    `json:"sqs_details" db:"sqs_details"`
	Status             string        `json:"status" db:"status"`
	Error              string        `json:"error" db:"error"`
	ServiceID          int           `json:"service_id" db:"service_id"`
	RetentionProfileID string        `json:"retention_profile_id" db:"retention_profile_id"`
	AWSLeaseID         sql.NullInt64 `json:"aws_lease_id" db:"aws_lease_id"`
}

type Subscription struct {
	ID                   int           `json:"id" db:"id"`
	Status               string        `json:"status" db:"status"`
	Error                string        `json:"error" db:"error"`
	SubscriptionTargetID int           `json:"subscription_target_id" db:"subscription_target_id"`
	EventStreamID        int           `json:"event_stream_id" db:"event_stream_id"`
	SNSSubscriptionARN   string        `json:"sns_subscription_arn" db:"sns_subscription_arn"`
	AWSLeaseID           sql.NullInt64 `json:"aws_lease_id" db:"aws_lease_id"`
}

type SQSDetails struct {
	SQSQueueARN string `json:"sqs_queue_arn" db:"sqs_queue_arn"`
	SQSQueueURL string `json:"sqs_queue_url" db:"sqs_queue_url"`

	DeadletterQueueARN string `json:"deadletter_queue_arn" db:"deadletter_queue_arn"`
	DeadletterQueueURL string `json:"deadletter_queue_url" db:"deadletter_queue_url"`
}

type SubscriptionTargetEditable struct {
	Name string
}

func (ste SubscriptionTargetEditable) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("name", ste.Name)
	return nil
}

type SubscriptionTargetInfraUpdate struct {
	Status     string
	Error      string
	SQSDetails SQSDetails
}

type SubscriptionEditable struct{}

type SubscriptionInfraUpdate struct {
	Status             string
	Error              string
	SNSSubscriptionARN string
}

func (s Subscription) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddInt("id", s.ID)
	enc.AddString("status", s.Status)
	enc.AddString("error", s.Error)
	enc.AddInt("event_stream_id", s.EventStreamID)
	enc.AddInt("subscription_target_id", s.SubscriptionTargetID)
	enc.AddString("sns_subscription_arn", s.SNSSubscriptionARN)
	if s.AWSLeaseID.Valid {
		enc.AddInt64("aws_lease_id", s.AWSLeaseID.Int64)
	} else {
		enc.AddString("aws_lease_id", "")
	}
	return nil
}

func (s SubscriptionInfraUpdate) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("status", s.Status)
	enc.AddString("error", s.Error)
	enc.AddString("sns_subscription_arn", s.SNSSubscriptionARN)
	return nil
}

func (t SubscriptionTarget) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddInt("id", t.ID)
	enc.AddString("name", t.Name)
	enc.AddString("assume_role_arn", t.AssumeRoleARN)
	enc.AddString("status", t.Status)
	enc.AddString("error", t.Error)
	enc.AddInt("service_id", t.ServiceID)
	if t.AWSLeaseID.Valid {
		enc.AddInt64("aws_lease_id", t.AWSLeaseID.Int64)
	} else {
		enc.AddString("aws_lease_id", "")
	}
	enc.AddString("sqs_queue_arn", t.SQSDetails.SQSQueueARN)
	return nil
}

func (t SubscriptionTargetInfraUpdate) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("status", t.Status)
	enc.AddString("error", t.Error)
	enc.AddString("sqs_queue_arn", t.SQSDetails.SQSQueueARN)
	return nil
}

type SubscriptionTargetsDB interface {
	SubscriptionTargetCreate(ctx context.Context, subscriptionTarget *SubscriptionTarget) (int, error)

	SubscriptionTargetUpdate(ctx context.Context, id int, subscriptionTargetEditable *SubscriptionTargetEditable) (int, error)
	SubscriptionTargetUpdateInfra(ctx context.Context, lease AWSLease, id int, subscriptionTargetInfraUpdate *SubscriptionTargetInfraUpdate) (int, error)

	SubscriptionTargetDelete(ctx context.Context, lease AWSLease, id int) error

	SubscriptionTargets(ctx context.Context) ([]*SubscriptionTarget, error)
	SubscriptionTargetByID(ctx context.Context, id int) (*SubscriptionTarget, error)
	SubscriptionTargetByQueueURL(ctx context.Context, name string) (*SubscriptionTarget, error)
	SubscriptionTargetsByServiceID(ctx context.Context, serviceID int) ([]*SubscriptionTarget, error)

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

type SubscriptionsDB interface {
	SubscriptionCreate(ctx context.Context, subscription *Subscription) (int, error)

	SubscriptionUpdateInfra(ctx context.Context, lease AWSLease, id int, subscriptionInfraUpdate *SubscriptionInfraUpdate) (int, error)

	SubscriptionDelete(ctx context.Context, lease AWSLease, subscriptionID int) error

	Subscriptions(ctx context.Context) ([]*Subscription, error)
	SubscriptionByID(ctx context.Context, id int) (*Subscription, error)
	SubscriptionsBySubscriptionTargetID(ctx context.Context, subscriptionTargetID int) ([]*Subscription, error)
	SubscriptionsByEventStreamID(ctx context.Context, eventStreamID int) ([]*Subscription, error)
	SubscriptionByEventStreamIDAndSubscriptionTargetID(ctx context.Context, eventStreamID int, subscriptionTargetID int) (*Subscription, error)

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