package integrations

import (
	"context"
	"fmt"
	"strings"

	"github.com/jmoiron/sqlx"
	"go.signoz.io/signoz/pkg/query-service/model"
)

func InitSqliteDBIfNeeded(db *sqlx.DB) error {
	if db == nil {
		return fmt.Errorf("db is required")
	}

	createTablesStatements := `
		CREATE TABLE IF NOT EXISTS integrations_installed(
			integration_id TEXT PRIMARY KEY,
			config_json TEXT,
			installed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
		)
	`
	_, err := db.Exec(createTablesStatements)
	if err != nil {
		return fmt.Errorf(
			"could not ensure integrations schema in sqlite DB: %w", err,
		)
	}

	return nil
}

type InstalledIntegrationsSqliteRepo struct {
	db *sqlx.DB
}

func NewInstalledIntegrationsSqliteRepo(db *sqlx.DB) (
	*InstalledIntegrationsSqliteRepo, error,
) {
	err := InitSqliteDBIfNeeded(db)
	if err != nil {
		return nil, fmt.Errorf(
			"couldn't ensure sqlite schema for installed integrations: %w", err,
		)
	}

	return &InstalledIntegrationsSqliteRepo{
		db: db,
	}, nil
}

func (r *InstalledIntegrationsSqliteRepo) list(
	ctx context.Context,
) ([]InstalledIntegration, *model.ApiError) {
	integrations := []InstalledIntegration{}

	err := r.db.SelectContext(
		ctx, &integrations, `
			select
				integration_id,
				config_json,
				installed_at
			from integrations_installed
			order by installed_at
		`,
	)
	if err != nil {
		return nil, model.InternalError(fmt.Errorf(
			"could not query installed integrations: %w", err,
		))
	}
	return integrations, nil
}

func (r *InstalledIntegrationsSqliteRepo) get(
	ctx context.Context, integrationIds []string,
) (map[string]InstalledIntegration, *model.ApiError) {
	integrations := []InstalledIntegration{}

	idPlaceholders := []string{}
	idValues := []interface{}{}
	for _, id := range integrationIds {
		idPlaceholders = append(idPlaceholders, "?")
		idValues = append(idValues, id)
	}

	err := r.db.SelectContext(
		ctx, &integrations, fmt.Sprintf(`
			select
				integration_id,
				config_json,
				installed_at
			from integrations_installed
			where integration_id in (%s)`,
			strings.Join(idPlaceholders, ", "),
		),
		idValues...,
	)
	if err != nil {
		return nil, model.InternalError(fmt.Errorf(
			"could not query installed integrations: %w", err,
		))
	}

	result := map[string]InstalledIntegration{}
	for _, ii := range integrations {
		result[ii.IntegrationId] = ii
	}

	return result, nil
}

func (r *InstalledIntegrationsSqliteRepo) upsert(
	ctx context.Context,
	integrationId string,
	config InstalledIntegrationConfig,
) (*InstalledIntegration, *model.ApiError) {
	serializedConfig, err := config.Value()
	if err != nil {
		return nil, model.BadRequest(fmt.Errorf(
			"could not serialize integration config: %w", err,
		))
	}

	_, dbErr := r.db.ExecContext(
		ctx, `
			INSERT INTO integrations_installed (
				integration_id,
				config_json
			) values ($1, $2)
			on conflict(integration_id) do update
				set config_json=excluded.config_json
		`, integrationId, serializedConfig,
	)
	if dbErr != nil {
		return nil, model.InternalError(fmt.Errorf(
			"could not insert record for integration installation: %w", dbErr,
		))
	}

	res, apiErr := r.get(ctx, []string{integrationId})
	if apiErr != nil || len(res) < 1 {
		return nil, model.WrapApiError(
			apiErr, "could not fetch installed integration",
		)
	}

	installed := res[integrationId]

	return &installed, nil
}

func (r *InstalledIntegrationsSqliteRepo) delete(
	ctx context.Context, integrationId string,
) *model.ApiError {
	_, dbErr := r.db.ExecContext(ctx, `
		DELETE FROM integrations_installed where integration_id = ?
	`, integrationId)

	if dbErr != nil {
		return model.InternalError(fmt.Errorf(
			"could not delete installed integration record for %s: %w",
			integrationId, dbErr,
		))
	}

	return nil
}
