package pipeline

import (
	"fmt"
	"io"
	"regexp"

	"github.com/pkg/errors"
)

type (
	MaterializerFunc        func(task *Asset, query string) (string, error)
	AssetMaterializationMap map[MaterializationType]map[MaterializationStrategy]MaterializerFunc
)

type Materializer struct {
	MaterializationMap AssetMaterializationMap
	FullRefresh        bool
}

func (m *Materializer) Render(asset *Asset, query string) (string, error) {
	mat := asset.Materialization
	if mat.Type == MaterializationTypeNone {
		return removeComments(query), nil
	}

	strategy := mat.Strategy
	if m.FullRefresh && mat.Type == MaterializationTypeTable {
		if mat.Strategy != MaterializationStrategyDDL {
			strategy = MaterializationStrategyCreateReplace
		}
	}

	if matFunc, ok := m.MaterializationMap[mat.Type][strategy]; ok {
		materializedQuery, err := matFunc(asset, query)
		if err != nil {
			return "", err
		}

		return removeComments(materializedQuery), nil
	}

	return "", fmt.Errorf("unsupported materialization type - strategy combination: (`%s` - `%s`)", mat.Type, mat.Strategy)
}

func removeComments(query string) string {
	bytes := []byte(query)
	re := regexp.MustCompile(`/\* *@bruin[\s\w\S]*@bruin *\*/`)
	newBytes := re.ReplaceAll(bytes, []byte(""))
	return string(newBytes)
}

func (m *Materializer) IsFullRefresh() bool {
	return m.FullRefresh
}

func (m *Materializer) LogIfFullRefreshAndDDL(writer interface{}, asset *Asset) error {
	if !m.FullRefresh {
		return nil
	}

	if asset.Materialization.Strategy != MaterializationStrategyDDL {
		return nil
	}
	if writer == nil {
		return errors.New("no writer found in context, please create an issue for this: https://github.com/bruin-data/bruin/issues")
	}
	message := "Full refresh detected, but DDL strategy is in use — table will NOT be dropped or recreated.\n"
	writerObj, ok := writer.(io.Writer)
	if !ok {
		return errors.New("writer is not an io.Writer")
	}
	_, _ = writerObj.Write([]byte(message))

	return nil
}
