package memory

import (
	"context"
	"excalidraw-complete/core"
	"fmt"
	"sync"
	"time"

	"github.com/oklog/ulid/v2"
	"github.com/sirupsen/logrus"
)

var (
	savedDocuments = make(map[string]core.Document)
	// savedCanvases is a map where the key is userID, and the value is another map
	// where the key is canvasID and the value is the canvas itself.
	savedCanvases = make(map[string]map[string]*core.Canvas)
	mu            sync.RWMutex
)

// memStore implements both DocumentStore and CanvasStore for in-memory storage.
type memStore struct{}

// NewStore creates a new in-memory store.
func NewStore() *memStore {
	return &memStore{}
}

// FindID retrieves a document by its ID. Part of the DocumentStore interface.
func (s *memStore) FindID(ctx context.Context, id string) (*core.Document, error) {
	mu.RLock()
	defer mu.RUnlock()

	log := logrus.WithField("document_id", id)
	if val, ok := savedDocuments[id]; ok {
		log.Info("Document retrieved successfully")
		return &val, nil
	}
	log.WithField("error", "document not found").Warn("Document with specified ID not found")
	return nil, fmt.Errorf("document with id %s not found", id)
}

// Create stores a new document. Part of the DocumentStore interface.
func (s *memStore) Create(ctx context.Context, document *core.Document) (string, error) {
	mu.Lock()
	defer mu.Unlock()

	id := ulid.Make().String()
	savedDocuments[id] = *document
	log := logrus.WithFields(logrus.Fields{
		"document_id": id,
		"data_length": len(document.Data.Bytes()),
	})
	log.Info("Document created successfully")

	return id, nil
}

// List returns metadata for all canvases owned by a user. Part of the CanvasStore interface.
func (s *memStore) List(ctx context.Context, userID string) ([]*core.Canvas, error) {
	mu.RLock()
	defer mu.RUnlock()

	userCanvases, ok := savedCanvases[userID]
	if !ok {
		return []*core.Canvas{}, nil // No canvases for this user, return empty slice
	}

	canvases := make([]*core.Canvas, 0, len(userCanvases))
	for _, canvas := range userCanvases {
		// Important: create a copy without the large `Data` field for the list view
		listCanvas := &core.Canvas{
			ID:        canvas.ID,
			UserID:    canvas.UserID,
			Name:      canvas.Name,
			Thumbnail: canvas.Thumbnail,
			CreatedAt: canvas.CreatedAt,
			UpdatedAt: canvas.UpdatedAt,
		}
		canvases = append(canvases, listCanvas)
	}

	logrus.WithField("user_id", userID).Infof("Listed %d canvases", len(canvases))
	return canvases, nil
}

// Get returns a single canvas by its ID, ensuring it belongs to the user. Part of the CanvasStore interface.
func (s *memStore) Get(ctx context.Context, userID, id string) (*core.Canvas, error) {
	mu.RLock()
	defer mu.RUnlock()

	log := logrus.WithFields(logrus.Fields{"user_id": userID, "canvas_id": id})

	userCanvases, ok := savedCanvases[userID]
	if !ok {
		log.Warn("User has no canvases")
		return nil, fmt.Errorf("canvas with id %s not found for user %s", id, userID)
	}

	canvas, ok := userCanvases[id]
	if !ok {
		log.Warn("Canvas not found for user")
		return nil, fmt.Errorf("canvas with id %s not found for user %s", id, userID)
	}

	log.Info("Canvas retrieved successfully")
	return canvas, nil
}

// Save creates or updates a canvas for a user. Part of the CanvasStore interface.
func (s *memStore) Save(ctx context.Context, canvas *core.Canvas) error {
	mu.Lock()
	defer mu.Unlock()

	log := logrus.WithFields(logrus.Fields{"user_id": canvas.UserID, "canvas_id": canvas.ID})

	if canvas.UserID == "" {
		return fmt.Errorf("UserID cannot be empty")
	}

	userCanvases, ok := savedCanvases[canvas.UserID]
	if !ok {
		userCanvases = make(map[string]*core.Canvas)
		savedCanvases[canvas.UserID] = userCanvases
	}

	if canvas.ID == "" {
		return fmt.Errorf("Canvas ID cannot be empty for save operation")
	}

	now := time.Now()
	if existingCanvas, exists := userCanvases[canvas.ID]; exists {
		canvas.CreatedAt = existingCanvas.CreatedAt
		canvas.UpdatedAt = now
	} else {
		canvas.CreatedAt = now
		canvas.UpdatedAt = now
	}

	userCanvases[canvas.ID] = canvas
	log.Info("Canvas saved successfully")
	return nil
}

// Delete removes a canvas, ensuring it belongs to the user. Part of the CanvasStore interface.
func (s *memStore) Delete(ctx context.Context, userID, id string) error {
	mu.Lock()
	defer mu.Unlock()

	log := logrus.WithFields(logrus.Fields{"user_id": userID, "canvas_id": id})

	userCanvases, ok := savedCanvases[userID]
	if !ok {
		log.Warn("User has no canvases to delete from")
		return fmt.Errorf("user %s has no canvases", userID)
	}

	if _, ok := userCanvases[id]; !ok {
		log.Warn("Canvas not found for deletion")
		return fmt.Errorf("canvas with id %s not found for user %s", id, userID)
	}

	delete(userCanvases, id)
	log.Info("Canvas deleted successfully")
	return nil
}
