package db

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

	"code.justin.tv/eventbus/controlplane/internal/containers"
	"go.uber.org/zap/zapcore"
)

type IAMRole struct {
	ID                   int           `json:"id" db:"id"`
	ARN                  string        `json:"arn" db:"arn"`
	Label                string        `json:"label" db:"label"`
	ServiceID            int           `json:"service_id" db:"service_id"`
	CloudformationStatus string        `json:"cloudformation_status" db:"cloudformation_status"`
	KMSGrantID           string        `json:"kms_grant_id" db:"kms_grant_id"`
	AWSLeaseID           sql.NullInt64 `json:"aws_lease_id" db:"aws_lease_id"`
}

func (i IAMRole) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddInt("id", i.ID)
	enc.AddString("iamRole", i.ARN)
	enc.AddString("label", i.Label)
	enc.AddInt("serviceID", i.ServiceID)
	enc.AddString("kms_grant_id", i.KMSGrantID)
	enc.AddString("cloudformationStatus", i.CloudformationStatus)
	return nil
}

func (i IAMRole) Editable() IAMRoleEditable {
	return IAMRoleEditable{
		Label: i.Label,
	}
}

type IAMRoleEditable struct {
	Label string
}

func (ir *IAMRoleEditable) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	enc.AddString("label", ir.Label)
	return nil
}

type IAMRolesEditable map[string]*IAMRoleEditable

func (ir IAMRolesEditable) MarshalLogObject(enc zapcore.ObjectEncoder) error {
	for arn, editable := range ir {
		enc.AddString("arn", arn)
		enc.AddString("label", editable.Label)
	}
	return nil
}

func (ir IAMRolesEditable) GetARNs() containers.StringSet {
	output := make(containers.StringSet, len(ir))
	for k := range ir {
		output.Add(k)
	}
	return output
}

type IAMRolesDB interface {
	IAMRoleCreate(context.Context, *IAMRole) (int, error)
	IAMRoleUpdate(context.Context, int, *IAMRoleEditable) (int, error)
	IAMRoles(context.Context) ([]*IAMRole, error)
	IAMRolesByServiceID(context.Context, int) ([]*IAMRole, error)
	IAMRoleUpdateCloudformationStatus(context.Context, int, string) (int, error)
	IAMRoleByARN(context.Context, string) (*IAMRole, error)
	IAMRoleByID(context.Context, int) (*IAMRole, error)
	IAMRoleDelete(context.Context, AWSLease, int) error
	IAMRoleAcquireLease(context.Context, int, time.Duration) (AWSLease, context.Context, error)
	IAMRoleReleaseLease(AWSLease) error
	IAMRolesEditForService(ctx context.Context, serviceID int, desiredRoles IAMRolesEditable) (report *IAMRolesEditReport, err error)
}

type IAMRolesEditReport struct {
	Added   containers.StringSet
	Removed containers.StringSet
}
