package mongo

import (
	"context"
	"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"
)

type JobExecutionStore struct {
	collection *mongodriver.Collection
}

func NewJobExecutionStore(db *mongodriver.Database) *JobExecutionStore {
	return &JobExecutionStore{
		collection: db.Collection("job_executions"),
	}
}

func (s *JobExecutionStore) List(ctx context.Context) ([]model.JobExecution, error) {
	findCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	cursor, err := s.collection.Find(
		findCtx,
		bson.D{},
		options.Find().SetSort(bson.D{{Key: "createdAt", Value: -1}}),
	)
	if err != nil {
		return nil, err
	}
	defer cursor.Close(findCtx)

	items := make([]model.JobExecution, 0)
	for cursor.Next(findCtx) {
		var item model.JobExecution
		if err := cursor.Decode(&item); err != nil {
			return nil, err
		}
		items = append(items, item)
	}

	if err := cursor.Err(); err != nil {
		return nil, err
	}

	return items, nil
}

func (s *JobExecutionStore) GetByID(ctx context.Context, id primitive.ObjectID) (*model.JobExecution, error) {
	findCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	var item model.JobExecution
	err := s.collection.FindOne(findCtx, bson.M{"_id": id}).Decode(&item)
	if err != nil {
		if errors.Is(err, mongodriver.ErrNoDocuments) {
			return nil, nil
		}
		return nil, err
	}

	return &item, nil
}

func (s *JobExecutionStore) Create(ctx context.Context, item *model.JobExecution) error {
	insertCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	if item.CreatedAt.IsZero() {
		item.CreatedAt = time.Now()
	}

	result, err := s.collection.InsertOne(insertCtx, item)
	if err != nil {
		return err
	}

	if oid, ok := result.InsertedID.(primitive.ObjectID); ok {
		item.ID = oid
	}

	return nil
}

func (s *JobExecutionStore) UpdateResult(ctx context.Context, item *model.JobExecution) error {
	updateCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()

	_, err := s.collection.UpdateByID(updateCtx, item.ID, bson.M{
		"$set": bson.M{
			"status":        item.Status,
			"startedAt":     item.StartedAt,
			"finishedAt":    item.FinishedAt,
			"durationMs":    item.DurationMs,
			"resultSummary": item.ResultSummary,
			"errorMessage":  item.ErrorMessage,
			"logText":       item.LogText,
			"logFile":       item.LogFile,
		},
	})
	return err
}
