package collection

import (
	"encoding/json"
	"fmt"
	"strings"

	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/pkg/storage"
	"codeberg.org/mapleopentech/monorepo/native/desktop/maplefile/internal/domain/collection"
)

const (
	collectionKeyPrefix = "collection:"
	parentCollIndex     = "parent_collection_index:"
	rootCollIndex       = "root_collection_index:"
)

type repository struct {
	storage storage.Storage
}

// ProvideRepository creates a new collection repository for Wire
func ProvideRepository(storage storage.Storage) collection.Repository {
	return &repository{storage: storage}
}

func (r *repository) Create(c *collection.Collection) error {
	return r.save(c)
}

func (r *repository) Get(id string) (*collection.Collection, error) {
	key := collectionKeyPrefix + id
	data, err := r.storage.Get(key)
	if err != nil {
		return nil, fmt.Errorf("failed to get collection: %w", err)
	}
	if data == nil {
		return nil, nil
	}

	var c collection.Collection
	if err := json.Unmarshal(data, &c); err != nil {
		return nil, fmt.Errorf("failed to unmarshal collection: %w", err)
	}

	return &c, nil
}

func (r *repository) Update(c *collection.Collection) error {
	// Get existing collection to clean up old indexes if parent changed
	existing, err := r.Get(c.ID)
	if err != nil {
		return fmt.Errorf("failed to get existing collection: %w", err)
	}
	if existing != nil && existing.ParentID != c.ParentID {
		// Clean up old parent index
		if existing.ParentID == "" {
			oldRootKey := rootCollIndex + existing.ID
			_ = r.storage.Delete(oldRootKey)
		} else {
			oldParentKey := parentCollIndex + existing.ParentID + ":" + existing.ID
			_ = r.storage.Delete(oldParentKey)
		}
	}

	return r.save(c)
}

func (r *repository) Delete(id string) error {
	// Get collection first to remove indexes
	c, err := r.Get(id)
	if err != nil {
		return err
	}
	if c == nil {
		return nil // Nothing to delete
	}

	// Delete parent index or root index
	if c.ParentID == "" {
		rootKey := rootCollIndex + id
		if err := r.storage.Delete(rootKey); err != nil {
			return fmt.Errorf("failed to delete root index: %w", err)
		}
	} else {
		parentKey := parentCollIndex + c.ParentID + ":" + id
		if err := r.storage.Delete(parentKey); err != nil {
			return fmt.Errorf("failed to delete parent index: %w", err)
		}
	}

	// Delete collection
	collKey := collectionKeyPrefix + id
	if err := r.storage.Delete(collKey); err != nil {
		return fmt.Errorf("failed to delete collection: %w", err)
	}

	return nil
}

func (r *repository) List() ([]*collection.Collection, error) {
	var collections []*collection.Collection

	err := r.storage.Iterate(func(key, value []byte) error {
		keyStr := string(key)
		if strings.HasPrefix(keyStr, collectionKeyPrefix) {
			var c collection.Collection
			if err := json.Unmarshal(value, &c); err != nil {
				return fmt.Errorf("failed to unmarshal collection: %w", err)
			}
			collections = append(collections, &c)
		}
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to list collections: %w", err)
	}

	return collections, nil
}

func (r *repository) ListByParent(parentID string) ([]*collection.Collection, error) {
	var collections []*collection.Collection
	prefix := parentCollIndex + parentID + ":"

	err := r.storage.Iterate(func(key, value []byte) error {
		keyStr := string(key)
		if strings.HasPrefix(keyStr, prefix) {
			// Extract collection ID from index key
			collID := strings.TrimPrefix(keyStr, prefix)
			c, err := r.Get(collID)
			if err != nil {
				return err
			}
			if c != nil {
				collections = append(collections, c)
			}
		}
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to list collections by parent: %w", err)
	}

	return collections, nil
}

func (r *repository) ListRoot() ([]*collection.Collection, error) {
	var collections []*collection.Collection

	err := r.storage.Iterate(func(key, value []byte) error {
		keyStr := string(key)
		if strings.HasPrefix(keyStr, rootCollIndex) {
			// Extract collection ID from index key
			collID := strings.TrimPrefix(keyStr, rootCollIndex)
			c, err := r.Get(collID)
			if err != nil {
				return err
			}
			if c != nil {
				collections = append(collections, c)
			}
		}
		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed to list root collections: %w", err)
	}

	return collections, nil
}

func (r *repository) Exists(id string) (bool, error) {
	key := collectionKeyPrefix + id
	data, err := r.storage.Get(key)
	if err != nil {
		return false, fmt.Errorf("failed to check collection existence: %w", err)
	}
	return data != nil, nil
}

// save persists the collection and maintains indexes
func (r *repository) save(c *collection.Collection) error {
	data, err := json.Marshal(c)
	if err != nil {
		return fmt.Errorf("failed to marshal collection: %w", err)
	}

	// Save collection by ID
	collKey := collectionKeyPrefix + c.ID
	if err := r.storage.Set(collKey, data); err != nil {
		return fmt.Errorf("failed to save collection: %w", err)
	}

	// Create parent index (for ListByParent) or root index (for ListRoot)
	if c.ParentID == "" {
		rootKey := rootCollIndex + c.ID
		if err := r.storage.Set(rootKey, []byte(c.ID)); err != nil {
			return fmt.Errorf("failed to create root index: %w", err)
		}
	} else {
		parentKey := parentCollIndex + c.ParentID + ":" + c.ID
		if err := r.storage.Set(parentKey, []byte(c.ID)); err != nil {
			return fmt.Errorf("failed to create parent index: %w", err)
		}
	}

	return nil
}
