package postgres

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

	db "code.justin.tv/eventbus/controlplane/internal/db"
	"github.com/pkg/errors"
)

const (
	AuthorizedFieldSubscriberGrantsTableName = "authorized_field_subscriber_grants"

	authorizedFieldSubscriberGrantCreateQuery = `
INSERT INTO authorized_field_subscriber_grants (authorized_field_id, iam_role_id, kms_grant_id)
VALUES (:authorized_field_id, :iam_role_id, '')
RETURNING id
`

	authorizedFieldSubscriberGrantsAllQuery = `
SELECT * FROM authorized_field_subscriber_grants
`

	authorizedFieldSubscriberGrantByIDQuery = `
SELECT * FROM authorized_field_subscriber_grants
WHERE id = $1
`

	authorizedFieldSubscriberGrantsByIAMRoleIDQuery = `
SELECT * FROM authorized_field_subscriber_grants
WHERE iam_role_id = $1
`

	authorizedFieldSubscriberGrantUpdateInfraQuery = `
UPDATE authorized_field_subscriber_grants
SET kms_grant_id = $2
WHERE id = $1
`

	authorizedFieldSubscriberGrantQuery = `
SELECT * FROM authorized_field_subscriber_grants
WHERE iam_role_id = $1 AND authorized_field_id = $2
`

	authorizedFieldSubscriberGrantDeleteByIDQuery = `
DELETE FROM authorized_field_subscriber_grants
WHERE id = $1
`

	authorizedFieldSubscriberGrantListByAuthorizedFieldQuery = `
SELECT * FROM authorized_field_subscriber_grants
WHERE authorized_field_id = $1
`
)

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantCreate(ctx context.Context, grant *db.AuthorizedFieldSubscriberGrant) (int, error) {
	var id int
	namedQuery, err := pg.writer.PrepareNamedContext(ctx, authorizedFieldSubscriberGrantCreateQuery)
	if err != nil {
		return -1, err
	}

	err = namedQuery.QueryRowxContext(ctx, grant).Scan(&id)
	return id, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrant(ctx context.Context, iamRoleID, authorizedFieldID int) (*db.AuthorizedFieldSubscriberGrant, error) {
	var grant db.AuthorizedFieldSubscriberGrant
	err := pg.reader.GetContext(ctx, &grant, authorizedFieldSubscriberGrantQuery, iamRoleID, authorizedFieldID)
	if err == sql.ErrNoRows {
		return nil, pgError(err, AuthorizedFieldSubscriberGrantsTableName)
	}
	return &grant, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrants(ctx context.Context) ([]*db.AuthorizedFieldSubscriberGrant, error) {
	var grants []*db.AuthorizedFieldSubscriberGrant
	err := pg.reader.SelectContext(ctx, &grants, authorizedFieldSubscriberGrantsAllQuery)
	if err == sql.ErrNoRows {
		return []*db.AuthorizedFieldSubscriberGrant{}, nil
	}
	return grants, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantByID(ctx context.Context, id int) (*db.AuthorizedFieldSubscriberGrant, error) {
	var grant db.AuthorizedFieldSubscriberGrant
	err := pg.reader.GetContext(ctx, &grant, authorizedFieldSubscriberGrantByIDQuery, id)
	if err == sql.ErrNoRows {
		return nil, pgError(err, AuthorizedFieldSubscriberGrantsTableName)
	}
	return &grant, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantsByIAMRoleID(ctx context.Context, iamRoleID int) ([]*db.AuthorizedFieldSubscriberGrant, error) {
	var grants []*db.AuthorizedFieldSubscriberGrant
	err := pg.reader.SelectContext(ctx, &grants, authorizedFieldSubscriberGrantsByIAMRoleIDQuery, iamRoleID)
	if err == sql.ErrNoRows {
		return nil, db.ErrResourceNotFound
	}
	return grants, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantsByAuthorizedField(ctx context.Context, authedFieldID int) ([]*db.AuthorizedFieldSubscriberGrant, error) {
	var grants []*db.AuthorizedFieldSubscriberGrant
	err := pg.reader.SelectContext(ctx, &grants, authorizedFieldSubscriberGrantListByAuthorizedFieldQuery, authedFieldID)
	if err == sql.ErrNoRows {
		return []*db.AuthorizedFieldSubscriberGrant{}, nil
	}
	return grants, err
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantDeleteByID(ctx context.Context, id int) error {
	res, err := pg.writer.ExecContext(ctx, authorizedFieldSubscriberGrantDeleteByIDQuery, id)
	if err != nil {
		return err
	}

	rowsAffected, err := res.RowsAffected()
	if err != nil {
		return err
	}

	if rowsAffected == 0 {
		return db.ErrResourceNotFound
	}

	return nil
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantUpdateInfra(ctx context.Context, lease db.AWSLease, id int, update *db.AuthorizedFieldSubscriberGrantInfraUpdate) (int, error) {
	if lease == nil {
		return -1, errors.New("nil lease object passed into EventStreamsTable.Update")
	}

	if lease.Expired() {
		return -1, db.ErrLeaseExpired
	}

	result, err := pg.writer.ExecContext(ctx, authorizedFieldSubscriberGrantUpdateInfraQuery, id, update.KMSGrantID)
	if err != nil {
		return -1, err
	}
	i, err := result.RowsAffected()
	if err != nil {
		return -1, err
	} else if i == 0 {
		return -1, db.ErrResourceNotFound
	}
	return id, nil
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantAcquireLease(ctx context.Context, resourceID int, timeout time.Duration) (db.AWSLease, context.Context, error) {
	return pg.Acquire(ctx, resourceID, AuthorizedFieldSubscriberGrantsTableName, timeout)
}

func (pg *PostgresDB) AuthorizedFieldSubscriberGrantReleaseLease(lease db.AWSLease) error {
	return lease.Release()
}
