package prefimpl

import (
	"context"
	"errors"
	"time"

	"github.com/grafana/grafana/pkg/infra/db"
	pref "github.com/grafana/grafana/pkg/services/preference"
	"github.com/grafana/grafana/pkg/setting"
)

type Service struct {
	store    store
	defaults pref.Preference
}

func ProvideService(db db.DB, cfg *setting.Cfg) pref.Service {
	return &Service{
		store: &sqlStore{
			db: db,
		},
		defaults: prefsFromConfig(cfg),
	}
}

func prefsFromConfig(cfg *setting.Cfg) pref.Preference {
	return pref.Preference{
		Theme:            cfg.DefaultTheme,
		Timezone:         cfg.DateFormats.DefaultTimezone,
		WeekStart:        &cfg.DateFormats.DefaultWeekStart,
		HomeDashboardID:  0, // nolint:staticcheck
		HomeDashboardUID: "",
		JSONData: &pref.PreferenceJSONData{
			Language: cfg.DefaultLanguage,
		},
	}
}

func (s *Service) GetWithDefaults(ctx context.Context, query *pref.GetPreferenceWithDefaultsQuery) (*pref.Preference, error) {
	listQuery := &pref.Preference{
		Teams:  query.Teams,
		OrgID:  query.OrgID,
		UserID: query.UserID,
	}

	prefs, err := s.store.List(ctx, listQuery)
	if err != nil {
		return nil, err
	}

	res := s.GetDefaults()
	for _, p := range prefs {
		if p.Theme != "" {
			res.Theme = p.Theme
		}
		if p.Timezone != "" {
			res.Timezone = p.Timezone
		}
		if p.WeekStart != nil && *p.WeekStart != "" {
			res.WeekStart = p.WeekStart
		}
		// nolint: staticcheck
		if p.HomeDashboardID != 0 {
			res.HomeDashboardID = p.HomeDashboardID
		}
		if p.HomeDashboardUID != "" {
			res.HomeDashboardUID = p.HomeDashboardUID
		}
		if p.JSONData != nil {
			if p.JSONData.Language != "" {
				res.JSONData.Language = p.JSONData.Language
			}

			if p.JSONData.RegionalFormat != "" {
				res.JSONData.RegionalFormat = p.JSONData.RegionalFormat
			}

			if p.JSONData.QueryHistory.HomeTab != "" {
				res.JSONData.QueryHistory.HomeTab = p.JSONData.QueryHistory.HomeTab
			}

			if p.JSONData.Navbar.BookmarkUrls != nil {
				res.JSONData.Navbar.BookmarkUrls = p.JSONData.Navbar.BookmarkUrls
			}
		}
	}

	return res, err
}

func (s *Service) Get(ctx context.Context, query *pref.GetPreferenceQuery) (*pref.Preference, error) {
	getPref := &pref.Preference{
		OrgID:  query.OrgID,
		UserID: query.UserID,
		TeamID: query.TeamID,
	}
	prefs, err := s.store.Get(ctx, getPref)
	if errors.Is(err, pref.ErrPrefNotFound) {
		return &pref.Preference{}, nil
	}
	if err != nil {
		return nil, err
	}
	return prefs, nil
}

func (s *Service) Save(ctx context.Context, cmd *pref.SavePreferenceCommand) error {
	jsonData, err := preferenceData(cmd)
	if err != nil {
		return err
	}

	preference, err := s.store.Get(ctx, &pref.Preference{
		OrgID:  cmd.OrgID,
		UserID: cmd.UserID,
		TeamID: cmd.TeamID,
	})
	if err != nil {
		if errors.Is(err, pref.ErrPrefNotFound) {
			preference := &pref.Preference{
				UserID: cmd.UserID,
				OrgID:  cmd.OrgID,
				TeamID: cmd.TeamID,
				// nolint: staticcheck
				HomeDashboardID: cmd.HomeDashboardID,
				Timezone:        cmd.Timezone,
				WeekStart:       &cmd.WeekStart,
				Theme:           cmd.Theme,
				Created:         time.Now(),
				Updated:         time.Now(),
				JSONData:        jsonData,
			}

			if cmd.HomeDashboardUID != nil {
				preference.HomeDashboardUID = *cmd.HomeDashboardUID
			}

			_, err = s.store.Insert(ctx, preference)
			if err != nil {
				return err
			}
		}
		return err
	}

	preference.Timezone = cmd.Timezone
	preference.WeekStart = &cmd.WeekStart
	preference.Theme = cmd.Theme
	preference.Updated = time.Now()
	preference.Version += 1
	preference.HomeDashboardID = cmd.HomeDashboardID // nolint:staticcheck
	if cmd.HomeDashboardUID != nil {
		preference.HomeDashboardUID = *cmd.HomeDashboardUID
	}
	preference.JSONData = jsonData

	return s.store.Update(ctx, preference)
}

func (s *Service) Patch(ctx context.Context, cmd *pref.PatchPreferenceCommand) error {
	var exists bool
	preference, err := s.store.Get(ctx, &pref.Preference{
		OrgID:  cmd.OrgID,
		UserID: cmd.UserID,
		TeamID: cmd.TeamID,
	})
	if err != nil && !errors.Is(err, pref.ErrPrefNotFound) {
		return err
	}

	if errors.Is(err, pref.ErrPrefNotFound) {
		preference = &pref.Preference{
			UserID:   cmd.UserID,
			OrgID:    cmd.OrgID,
			TeamID:   cmd.TeamID,
			Created:  time.Now(),
			JSONData: &pref.PreferenceJSONData{},
		}
	} else {
		exists = true
	}

	if cmd.Language != nil {
		if preference.JSONData == nil {
			preference.JSONData = &pref.PreferenceJSONData{}
		}
		preference.JSONData.Language = *cmd.Language
	}

	if cmd.RegionalFormat != nil {
		if preference.JSONData == nil {
			preference.JSONData = &pref.PreferenceJSONData{}
		}
		preference.JSONData.RegionalFormat = *cmd.RegionalFormat
	}

	if cmd.Navbar != nil && cmd.Navbar.BookmarkUrls != nil {
		if preference.JSONData == nil {
			preference.JSONData = &pref.PreferenceJSONData{}
		}
		preference.JSONData.Navbar.BookmarkUrls = cmd.Navbar.BookmarkUrls
	}

	if cmd.QueryHistory != nil {
		if preference.JSONData == nil {
			preference.JSONData = &pref.PreferenceJSONData{}
		}
		if cmd.QueryHistory.HomeTab != "" {
			preference.JSONData.QueryHistory.HomeTab = cmd.QueryHistory.HomeTab
		}
	}

	// nolint: staticcheck
	if cmd.HomeDashboardID != nil {
		preference.HomeDashboardID = *cmd.HomeDashboardID
	}

	if cmd.HomeDashboardUID != nil {
		preference.HomeDashboardUID = *cmd.HomeDashboardUID
	}

	if cmd.Timezone != nil {
		preference.Timezone = *cmd.Timezone
	}

	if cmd.WeekStart != nil {
		preference.WeekStart = cmd.WeekStart
	}

	if cmd.Theme != nil {
		preference.Theme = *cmd.Theme
	}

	preference.Updated = time.Now()
	preference.Version += 1

	if exists {
		err = s.store.Update(ctx, preference)
	} else {
		_, err = s.store.Insert(ctx, preference)
	}
	return err
}

func (s *Service) GetDefaults() *pref.Preference {
	return &pref.Preference{
		Theme:            s.defaults.Theme,
		Timezone:         s.defaults.Timezone,
		WeekStart:        s.defaults.WeekStart,
		HomeDashboardID:  0, // nolint:staticcheck
		HomeDashboardUID: "",
		JSONData: &pref.PreferenceJSONData{
			Language: s.defaults.JSONData.Language,
		},
	}
}

func (s *Service) Delete(ctx context.Context, cmd *pref.DeleteCommand) error {
	return s.store.Delete(ctx, cmd)
}

func preferenceData(cmd *pref.SavePreferenceCommand) (*pref.PreferenceJSONData, error) {
	jsonData := &pref.PreferenceJSONData{
		Language:       cmd.Language,
		RegionalFormat: cmd.RegionalFormat,
	}
	if cmd.Navbar != nil {
		jsonData.Navbar = *cmd.Navbar
	}
	if cmd.QueryHistory != nil {
		jsonData.QueryHistory = *cmd.QueryHistory
	}

	return jsonData, nil
}
