package receiver

import (
	"bytes"
	"fmt"
	"log/slog"
	"strconv"
	"strings"
	"text/template"

	"github.com/spf13/viper"

	"github.com/tidbcloud/jiraport/pkg/clients/clinic"
	"github.com/tidbcloud/jiraport/pkg/clients/engineer"
	"github.com/tidbcloud/jiraport/pkg/entity"
	"github.com/tidbcloud/jiraport/pkg/matcher"
)

type GroupReceiver string

// impl Receiver
func (sr *GroupReceiver) Type() string {
	return GroupReceiverType
}

// impl Receiver
func (sr *GroupReceiver) ID() string {
	return string(*sr)
}

// impl Receiver
func (sr *GroupReceiver) Show() string {
	return string(*sr)
}

func (sr *GroupReceiver) BotType() string {
	return ""
}

type RegionCCReceiverSelector struct {
	logger           *slog.Logger
	regionReceivers  map[string]GroupReceiver
	tenantExtractor  *template.Template
	clusterExtractor *template.Template
	clinicClient     clinic.Interface
	engineerClient   engineer.Interface
}

func NewRegionCCReceiverSelector(
	logger *slog.Logger,
	cliniccli clinic.Interface,
	engineerClient engineer.Interface,
) (*RegionCCReceiverSelector, error) {
	var regionReceivers map[string]GroupReceiver
	// NOTE: all key will be lowercase, see https://github.com/spf13/viper/issues/1014
	err := viper.UnmarshalKey("region-receivers", &regionReceivers)
	if err != nil {
		return nil, err
	}

	parseExtractor := func(e string) (*template.Template, error) {
		tmpl := template.New("").Option("missingkey=zero").Funcs(matcher.CommonFuncs)
		tmpl, err = tmpl.Parse(e)
		if err != nil {
			return nil, fmt.Errorf("parse error in %#v, %w", e, err)
		}
		return tmpl, nil
	}
	tenantExtractor, err := parseExtractor(viper.GetString("tenant-extractor"))
	if err != nil {
		return nil, err
	}
	clusterExtractor, err := parseExtractor(viper.GetString("cluster-extractor"))
	if err != nil {
		return nil, err
	}

	return &RegionCCReceiverSelector{
		logger:           logger,
		regionReceivers:  regionReceivers,
		tenantExtractor:  tenantExtractor,
		clusterExtractor: clusterExtractor,
		clinicClient:     cliniccli,
		engineerClient:   engineerClient,
	}, nil
}

func (rs *RegionCCReceiverSelector) SelectReceiver(name string, issue *entity.Issue) ([]Receiver, error) {
	if name != "regioncc" {
		return nil, nil
	}
	rawIssue := &issue.RawIssue

	var buf bytes.Buffer
	err := rs.tenantExtractor.Execute(&buf, rawIssue)
	if err != nil {
		return nil, err
	}
	tenant := buf.String()

	buf.Reset()
	err = rs.clusterExtractor.Execute(&buf, rawIssue)
	if err != nil {
		return nil, err
	}
	cluster := buf.String()

	receivers := make([]Receiver, 0)

	if !isNumeric(tenant) {
		tenant = ""
	}
	// select group
	region, err := rs.clinicClient.GetRegionByTenantIDAndClusterID(tenant, cluster)
	if err != nil {
		return nil, fmt.Errorf("get region by by tenent and cluster error, %w", err)
	}
	// NOTE: msut use lowercase region, see https://github.com/spf13/viper/issues/1014
	region = strings.ToLower(region)
	regionReceiver, ok := rs.regionReceivers[region]
	if !ok {
		rs.logger.Error("not found region in region receivers", "region", region)
	} else {
		receivers = append(receivers, &regionReceiver)
	}

	// select person contacts
	engineers, err := rs.GetContactsByRegion(region)
	if err != nil {
		return receivers, nil
	} else {
		for i := range engineers {
			receivers = append(receivers, &engineers[i])
		}
	}
	return receivers, nil
}

func (rs *RegionCCReceiverSelector) GetContactsByRegion(region string) ([]engineer.Engineer, error) {
	email, err := rs.clinicClient.GetRegionContactByRegion(region)
	if err != nil {
		return nil, fmt.Errorf("get contact by region error, %w", err)
	}
	contact, err := rs.engineerClient.GetEngineerByEmail(email)
	if err != nil {
		return nil, fmt.Errorf("get contact by email error, %w", err)
	}

	return []engineer.Engineer{*contact}, nil
}

func isNumeric(str string) bool {
	_, err := strconv.Atoi(str)
	return err == nil
}
