package mongo

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	mongodriver "go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"scheduler-backend/internal/store/model"
)

const defaultAdminPassword = "admin123"

type AdminProfileStore struct {
	collection *mongodriver.Collection
}

func NewAdminProfileStore(db *mongodriver.Database) *AdminProfileStore {
	return &AdminProfileStore{
		collection: db.Collection("admin_profiles"),
	}
}

func (s *AdminProfileStore) Get(ctx context.Context) (*model.AdminProfile, error) {
	findCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	var item model.AdminProfile
	err := s.collection.FindOne(findCtx, bson.D{}, options.FindOne().SetSort(bson.D{{Key: "updatedAt", Value: -1}})).Decode(&item)
	if err == nil {
		return &item, nil
	}
	if !errors.Is(err, mongodriver.ErrNoDocuments) {
		return nil, err
	}

	now := time.Now()
	item = model.AdminProfile{
		ID:           primitive.NewObjectID(),
		Account:      "admin",
		Nickname:     "调度管理员",
		Avatar:       "",
		Remark:       "default admin profile",
		PasswordHash: hashPassword(defaultAdminPassword),
		CreatedAt:    now,
		UpdatedAt:    now,
	}
	if _, err := s.collection.InsertOne(findCtx, item); err != nil {
		return nil, err
	}
	return &item, nil
}

func (s *AdminProfileStore) Upsert(ctx context.Context, profile *model.AdminProfile) error {
	updateCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	now := time.Now()
	profile.UpdatedAt = now
	if profile.CreatedAt.IsZero() {
		profile.CreatedAt = now
	}

	if profile.ID.IsZero() {
		current, err := s.Get(ctx)
		if err != nil {
			return err
		}
		profile.ID = current.ID
		if profile.PasswordHash == "" {
			profile.PasswordHash = current.PasswordHash
		}
		if profile.CreatedAt.IsZero() {
			profile.CreatedAt = current.CreatedAt
		}
	}

	_, err := s.collection.UpdateByID(updateCtx, profile.ID, bson.M{
		"$set": bson.M{
			"account":      profile.Account,
			"nickname":     profile.Nickname,
			"avatar":       profile.Avatar,
			"remark":       profile.Remark,
			"passwordHash": profile.PasswordHash,
			"createdAt":    profile.CreatedAt,
			"updatedAt":    profile.UpdatedAt,
		},
	}, options.Update().SetUpsert(true))
	return err
}

func (s *AdminProfileStore) ChangePassword(ctx context.Context, oldPassword, newPassword string) error {
	current, err := s.Get(ctx)
	if err != nil {
		return err
	}
	if current.PasswordHash != hashPassword(oldPassword) {
		return ErrInvalidPassword
	}

	current.PasswordHash = hashPassword(newPassword)
	return s.Upsert(ctx, current)
}

func (s *AdminProfileStore) Authenticate(ctx context.Context, account, password string) (*model.AdminProfile, error) {
	current, err := s.Get(ctx)
	if err != nil {
		return nil, err
	}
	if current.Account != account || current.PasswordHash != hashPassword(password) {
		return nil, ErrInvalidPassword
	}
	return current, nil
}

func hashPassword(password string) string {
	sum := sha256.Sum256([]byte(password))
	return hex.EncodeToString(sum[:])
}
