package repo

import (
	"context"
	"fmt"
	"time"

	calent "github.com/tidbcloud/devops/entity/k8scalendar"
	calmod "github.com/tidbcloud/devops/models/k8scalendar"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/pkgv2/db"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

type NotificationUpdate struct {
	NotifiedVersionAddedAt        *time.Time
	NotifiedEOSChangedFingerprint *string
	EOSApproachingNotifiedFor     *time.Time
}

// NotificationReset selects which notification markers to clear (set to zero/NULL).
type NotificationReset struct {
	ClearNotifiedVersionAddedAt        bool
	ClearNotifiedEOSChangedFingerprint bool
	ClearEOSApproachingNotifiedFor     bool
}

type Repo interface {
	UpsertLifecycles(ctx context.Context, items []*calent.VersionLifecycle) error
	ListLifecycles(ctx context.Context, cloudProvider string) ([]*calent.VersionLifecycle, error)
	GetLifecycle(ctx context.Context, cloud, version string) (*calent.VersionLifecycle, error)
	SaveSnapshots(ctx context.Context, items []*calent.VersionLifecycle) error
	UpdateNotification(ctx context.Context, cloud, version string, upd NotificationUpdate) error
	ResetNotifications(ctx context.Context, cloud, version string, reset NotificationReset) error
}

type repo struct {
	dbp db.Provider
}

func NewRepo(dbp db.Provider) Repo {
	return &repo{dbp: dbp}
}

func (r *repo) UpsertLifecycles(ctx context.Context, items []*calent.VersionLifecycle) error {
	if len(items) == 0 {
		return nil
	}
	mods := container.TransformSlice(items, entityToModel)
	return r.dbp.WriteDB(ctx).Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "cloud"}, {Name: "version"}},
		DoUpdates: clause.AssignmentColumns([]string{
			"cloud_provider", "lifecycle_start", "end_of_standard_support",
			"end_of_extended_support", "release_precision", "source_url", "fingerprint", "scraped_at", "updated_at",
		}),
	}).Create(&mods).Error
}

func (r *repo) ListLifecycles(ctx context.Context, cloudProvider string) ([]*calent.VersionLifecycle, error) {
	var mods []*calmod.VersionLifecycle
	q := r.dbp.ReadDB(ctx).Model(&calmod.VersionLifecycle{})
	if cloudProvider != "" {
		q = q.Where("cloud_provider = ?", cloudProvider)
	}
	if err := q.Order("end_of_standard_support asc").Find(&mods).Error; err != nil {
		return nil, fmt.Errorf("list lifecycles: %w", err)
	}
	return container.TransformSlice(mods, modelToEntity), nil
}

func (r *repo) GetLifecycle(ctx context.Context, cloud, version string) (*calent.VersionLifecycle, error) {
	var m calmod.VersionLifecycle
	err := r.dbp.ReadDB(ctx).Where("cloud = ? AND version = ?", cloud, version).First(&m).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	return modelToEntity(&m), nil
}

func (r *repo) SaveSnapshots(ctx context.Context, items []*calent.VersionLifecycle) error {
	if len(items) == 0 {
		return nil
	}
	snaps := make([]*calmod.VersionLifecycleSnapshot, 0, len(items))
	for _, it := range items {
		snaps = append(snaps, &calmod.VersionLifecycleSnapshot{
			Cloud:                it.Cloud,
			Version:              it.Version,
			CloudProvider:        it.CloudProvider,
			LifecycleStart:       it.LifecycleStart,
			EndOfStandardSupport: it.EndOfStandardSupport,
			EndOfExtendedSupport: it.EndOfExtendedSupport,
			ReleasePrecision:     it.ReleasePrecision,
			Fingerprint:          it.Fingerprint,
			ScrapedAt:            it.ScrapedAt,
		})
	}
	return r.dbp.WriteDB(ctx).Create(&snaps).Error
}

func (r *repo) UpdateNotification(ctx context.Context, cloud, version string, upd NotificationUpdate) error {
	fields := map[string]any{}
	if upd.NotifiedVersionAddedAt != nil {
		fields["notified_version_added_at"] = *upd.NotifiedVersionAddedAt
	}
	if upd.NotifiedEOSChangedFingerprint != nil {
		fields["notified_eos_changed_fingerprint"] = *upd.NotifiedEOSChangedFingerprint
	}
	if upd.EOSApproachingNotifiedFor != nil {
		fields["eos_approaching_notified_for"] = *upd.EOSApproachingNotifiedFor
	}
	if len(fields) == 0 {
		return nil
	}
	res := r.dbp.WriteDB(ctx).Model(&calmod.VersionLifecycle{}).
		Where("cloud = ? AND version = ?", cloud, version).
		Updates(fields)
	if res.Error != nil {
		return fmt.Errorf("update notification: %w", res.Error)
	}
	if res.RowsAffected == 0 {
		return fmt.Errorf("update notification: lifecycle %s/%s not found", cloud, version)
	}
	return nil
}

func (r *repo) ResetNotifications(ctx context.Context, cloud, version string, reset NotificationReset) error {
	fields := map[string]any{}
	if reset.ClearNotifiedVersionAddedAt {
		fields["notified_version_added_at"] = nil
	}
	if reset.ClearNotifiedEOSChangedFingerprint {
		fields["notified_eos_changed_fingerprint"] = ""
	}
	if reset.ClearEOSApproachingNotifiedFor {
		fields["eos_approaching_notified_for"] = nil
	}
	if len(fields) == 0 {
		return nil
	}
	res := r.dbp.WriteDB(ctx).Model(&calmod.VersionLifecycle{}).
		Where("cloud = ? AND version = ?", cloud, version).
		Updates(fields)
	if res.Error != nil {
		return fmt.Errorf("reset notifications: %w", res.Error)
	}
	if res.RowsAffected == 0 {
		return fmt.Errorf("reset notifications: lifecycle %s/%s not found", cloud, version)
	}
	return nil
}

func entityToModel(e *calent.VersionLifecycle) *calmod.VersionLifecycle {
	return &calmod.VersionLifecycle{
		Cloud:                e.Cloud,
		Version:              e.Version,
		CloudProvider:        e.CloudProvider,
		LifecycleStart:       e.LifecycleStart,
		EndOfStandardSupport: e.EndOfStandardSupport,
		EndOfExtendedSupport: e.EndOfExtendedSupport,
		ReleasePrecision:     e.ReleasePrecision,
		SourceURL:            e.SourceURL,
		Fingerprint:          e.Fingerprint,
		ScrapedAt:            e.ScrapedAt,
	}
}

func modelToEntity(m *calmod.VersionLifecycle) *calent.VersionLifecycle {
	return &calent.VersionLifecycle{
		ID:                            m.ID.Uint64(),
		Cloud:                         m.Cloud,
		Version:                       m.Version,
		CloudProvider:                 m.CloudProvider,
		LifecycleStart:                m.LifecycleStart,
		EndOfStandardSupport:          m.EndOfStandardSupport,
		EndOfExtendedSupport:          m.EndOfExtendedSupport,
		ReleasePrecision:              m.ReleasePrecision,
		SourceURL:                     m.SourceURL,
		Fingerprint:                   m.Fingerprint,
		ScrapedAt:                     m.ScrapedAt,
		NotifiedVersionAddedAt:        m.NotifiedVersionAddedAt,
		NotifiedEOSChangedFingerprint: m.NotifiedEOSChangedFingerprint,
		EOSApproachingNotifiedFor:     m.EOSApproachingNotifiedFor,
	}
}
