package gorm

import (
	"github.com/porter-dev/porter/internal/encryption"
	"github.com/porter-dev/porter/internal/repository"
	"gorm.io/gorm"

	ints "github.com/porter-dev/porter/internal/models/integrations"
)

// SlackIntegrationRepository uses gorm.DB for querying the database
type SlackIntegrationRepository struct {
	db  *gorm.DB
	key *[32]byte
}

// NewSlackIntegrationRepository returns a SlackIntegrationRepository which uses
// gorm.DB for querying the database. It accepts an encryption key to encrypt
// sensitive data
func NewSlackIntegrationRepository(
	db *gorm.DB,
	key *[32]byte,
) repository.SlackIntegrationRepository {
	return &SlackIntegrationRepository{db, key}
}

// CreateSlackIntegration creates a new kube auth mechanism
func (repo *SlackIntegrationRepository) CreateSlackIntegration(
	slackInt *ints.SlackIntegration,
) (*ints.SlackIntegration, error) {
	err := repo.EncryptSlackIntegrationData(slackInt, repo.key)
	if err != nil {
		return nil, err
	}

	if err := repo.db.Create(slackInt).Error; err != nil {
		return nil, err
	}

	return slackInt, nil
}

// ListSlackIntegrationsByProjectID finds all kube auth mechanisms
// for a given project id
func (repo *SlackIntegrationRepository) ListSlackIntegrationsByProjectID(
	projectID uint,
) ([]*ints.SlackIntegration, error) {
	slackInts := []*ints.SlackIntegration{}

	if err := repo.db.Where("project_id = ?", projectID).Find(&slackInts).Error; err != nil {
		return nil, err
	}

	for _, slackInt := range slackInts {
		repo.DecryptSlackIntegrationData(slackInt, repo.key)
	}

	return slackInts, nil
}

// DeleteSlackIntegration deletes a slack integration by ID
func (repo *SlackIntegrationRepository) DeleteSlackIntegration(
	integrationID uint,
) error {
	if err := repo.db.Where("id = ?", integrationID).Delete(&ints.SlackIntegration{}).Error; err != nil {
		return err
	}

	return nil
}

// EncryptSlackIntegrationData will encrypt the slack integration data before
// writing to the DB
func (repo *SlackIntegrationRepository) EncryptSlackIntegrationData(
	slackInt *ints.SlackIntegration,
	key *[32]byte,
) error {
	if len(slackInt.ClientID) > 0 {
		cipherData, err := encryption.Encrypt(slackInt.ClientID, key)
		if err != nil {
			return err
		}

		slackInt.ClientID = cipherData
	}

	if len(slackInt.AccessToken) > 0 {
		cipherData, err := encryption.Encrypt(slackInt.AccessToken, key)
		if err != nil {
			return err
		}

		slackInt.AccessToken = cipherData
	}

	if len(slackInt.RefreshToken) > 0 {
		cipherData, err := encryption.Encrypt(slackInt.RefreshToken, key)
		if err != nil {
			return err
		}

		slackInt.RefreshToken = cipherData
	}

	if len(slackInt.Webhook) > 0 {
		cipherData, err := encryption.Encrypt(slackInt.Webhook, key)
		if err != nil {
			return err
		}

		slackInt.Webhook = cipherData
	}

	return nil
}

// DecryptSlackIntegrationData will decrypt the slack integration data before
// returning it from the DB
func (repo *SlackIntegrationRepository) DecryptSlackIntegrationData(
	slackInt *ints.SlackIntegration,
	key *[32]byte,
) error {
	if len(slackInt.ClientID) > 0 {
		plaintext, err := encryption.Decrypt(slackInt.ClientID, key)
		if err != nil {
			return err
		}

		slackInt.ClientID = plaintext
	}

	if len(slackInt.AccessToken) > 0 {
		plaintext, err := encryption.Decrypt(slackInt.AccessToken, key)
		if err != nil {
			return err
		}

		slackInt.AccessToken = plaintext
	}

	if len(slackInt.RefreshToken) > 0 {
		plaintext, err := encryption.Decrypt(slackInt.RefreshToken, key)
		if err != nil {
			return err
		}

		slackInt.RefreshToken = plaintext
	}

	if len(slackInt.Webhook) > 0 {
		plaintext, err := encryption.Decrypt(slackInt.Webhook, key)
		if err != nil {
			return err
		}

		slackInt.Webhook = plaintext
	}

	return nil
}
