package postgres

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

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

const (
	AuthorizedFieldPublisherGrantsTableName = "authorized_field_publisher_grants"

	authorizedFieldPublisherGrantCreateQuery = `
INSERT INTO authorized_field_publisher_grants (event_stream_id, iam_role_id, kms_grant_id)
VALUES (:event_stream_id, :iam_role_id, '')
RETURNING id
`

	authorizedFieldPublisherGrantsAllQuery = `
SELECT * FROM authorized_field_publisher_grants;
`

	authorizedFieldPublisherGrantQuery = `
SELECT * FROM authorized_field_publisher_grants
WHERE iam_role_id = $1 AND event_stream_id = $2
`

	authorizedFieldPublisherGrantByIDQuery = `
SELECT * FROM authorized_field_publisher_grants
WHERE id = $1
`

	authorizedFieldPublisherGrantsByIAMRoleIDQuery = `
SELECT * FROM authorized_field_publisher_grants
WHERE iam_role_id = $1
`

	authorizedFieldPublisherGrantsByEventStreamIDQuery = `
SELECT * FROM authorized_field_publisher_grants
WHERE event_stream_id = $1
`

	authorizedFieldPublisherGrantUpdateInfraQuery = `
UPDATE authorized_field_publisher_grants
SET kms_grant_id = $2
WHERE id = $1
`

	authorizedFieldPublisherGrantDeleteByIDQuery = `
DELETE FROM authorized_field_publisher_grants
WHERE id = $1
`
)

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

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

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

func (pg *PostgresDB) AuthorizedFieldPublisherGrant(ctx context.Context, iamRoleID, eventStreamID int) (*db.AuthorizedFieldPublisherGrant, error) {
	var grant db.AuthorizedFieldPublisherGrant
	err := pg.reader.GetContext(ctx, &grant, authorizedFieldPublisherGrantQuery, iamRoleID, eventStreamID)
	if err != nil {
		return nil, pgError(err, AuthorizedFieldPublisherGrantsTableName)
	}
	return &grant, err
}

func (pg *PostgresDB) AuthorizedFieldPublisherGrantByID(ctx context.Context, id int) (*db.AuthorizedFieldPublisherGrant, error) {
	var grant db.AuthorizedFieldPublisherGrant
	err := pg.reader.GetContext(ctx, &grant, authorizedFieldPublisherGrantByIDQuery, id)
	if err == sql.ErrNoRows {
		return nil, db.ErrResourceNotFound
	}
	return &grant, err
}

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

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

func (pg *PostgresDB) AuthorizedFieldPublisherGrantDeleteByID(ctx context.Context, id int) error {
	res, err := pg.writer.ExecContext(ctx, authorizedFieldPublisherGrantDeleteByIDQuery, 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) AuthorizedFieldPublisherGrantUpdateInfra(ctx context.Context, lease db.AWSLease, id int, update *db.AuthorizedFieldPublisherGrantInfraUpdate) (int, error) {
	if lease == nil {
		return -1, errors.New("nil lease object passed into AuthorizedFieldPublisherGrantsTable.Update")
	}

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

	result, err := pg.writer.ExecContext(ctx, authorizedFieldPublisherGrantUpdateInfraQuery, 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) AuthorizedFieldPublisherGrantAcquireLease(ctx context.Context, resourceID int, timeout time.Duration) (db.AWSLease, context.Context, error) {
	return pg.Acquire(ctx, resourceID, AuthorizedFieldPublisherGrantsTableName, timeout)
}

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