package db import ( "context" "database/sql" "fmt" "github.com/insertr/insertr/internal/db/sqlite" ) // SQLiteRepository implements ContentRepository for SQLite databases type SQLiteRepository struct { queries *sqlite.Queries db *sql.DB } // NewSQLiteRepository creates a new SQLite repository func NewSQLiteRepository(db *sql.DB) *SQLiteRepository { return &SQLiteRepository{ queries: sqlite.New(db), db: db, } } // GetContent retrieves a single content item func (r *SQLiteRepository) GetContent(ctx context.Context, siteID, contentID string) (*ContentItem, error) { content, err := r.queries.GetContent(ctx, sqlite.GetContentParams{ ID: contentID, SiteID: siteID, }) if err != nil { return nil, err } return &ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), LastEditedBy: content.LastEditedBy, }, nil } // GetBulkContent retrieves multiple content items func (r *SQLiteRepository) GetBulkContent(ctx context.Context, siteID string, contentIDs []string) (map[string]ContentItem, error) { contents, err := r.queries.GetBulkContent(ctx, sqlite.GetBulkContentParams{ SiteID: siteID, Ids: contentIDs, }) if err != nil { return nil, err } items := make(map[string]ContentItem) for _, content := range contents { items[content.ID] = ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), LastEditedBy: content.LastEditedBy, } } return items, nil } // GetAllContent retrieves all content items for a site func (r *SQLiteRepository) GetAllContent(ctx context.Context, siteID string) (map[string]ContentItem, error) { contents, err := r.queries.GetAllContent(ctx, siteID) if err != nil { return nil, err } items := make(map[string]ContentItem) for _, content := range contents { items[content.ID] = ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), LastEditedBy: content.LastEditedBy, } } return items, nil } // CreateContent creates a new content item func (r *SQLiteRepository) CreateContent(ctx context.Context, siteID, contentID, htmlContent, originalTemplate, lastEditedBy string) (*ContentItem, error) { content, err := r.queries.CreateContent(ctx, sqlite.CreateContentParams{ ID: contentID, SiteID: siteID, HtmlContent: htmlContent, OriginalTemplate: ToNullString(originalTemplate), LastEditedBy: lastEditedBy, }) if err != nil { return nil, err } return &ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: getStringFromNullString(content.OriginalTemplate), LastEditedBy: content.LastEditedBy, }, nil } // GetCollection retrieves a collection container func (r *SQLiteRepository) GetCollection(ctx context.Context, siteID, collectionID string) (*CollectionItem, error) { collection, err := r.queries.GetCollection(ctx, sqlite.GetCollectionParams{ ID: collectionID, SiteID: siteID, }) if err != nil { return nil, err } return &CollectionItem{ ID: collection.ID, SiteID: collection.SiteID, ContainerHTML: collection.ContainerHtml, LastEditedBy: collection.LastEditedBy, }, nil } // CreateCollection creates a new collection container func (r *SQLiteRepository) CreateCollection(ctx context.Context, siteID, collectionID, containerHTML, lastEditedBy string) (*CollectionItem, error) { collection, err := r.queries.CreateCollection(ctx, sqlite.CreateCollectionParams{ ID: collectionID, SiteID: siteID, ContainerHtml: containerHTML, LastEditedBy: lastEditedBy, }) if err != nil { return nil, err } return &CollectionItem{ ID: collection.ID, SiteID: collection.SiteID, ContainerHTML: collection.ContainerHtml, LastEditedBy: collection.LastEditedBy, }, nil } // GetCollectionItems retrieves all items in a collection with template information func (r *SQLiteRepository) GetCollectionItems(ctx context.Context, siteID, collectionID string) ([]CollectionItemWithTemplate, error) { items, err := r.queries.GetCollectionItemsWithTemplate(ctx, sqlite.GetCollectionItemsWithTemplateParams{ CollectionID: collectionID, SiteID: siteID, }) if err != nil { return nil, err } result := make([]CollectionItemWithTemplate, len(items)) for i, item := range items { result[i] = CollectionItemWithTemplate{ ItemID: item.ItemID, CollectionID: item.CollectionID, SiteID: item.SiteID, TemplateID: int(item.TemplateID), HTMLContent: item.HtmlContent, Position: int(item.Position), LastEditedBy: item.LastEditedBy, TemplateName: item.TemplateName, HTMLTemplate: item.HtmlTemplate, IsDefault: item.IsDefault != 0, // SQLite uses INTEGER for boolean } } return result, nil } // CreateCollectionTemplate creates a new template for a collection func (r *SQLiteRepository) CreateCollectionTemplate(ctx context.Context, siteID, collectionID, name, htmlTemplate string, isDefault bool) (*CollectionTemplateItem, error) { var isDefaultInt int64 if isDefault { isDefaultInt = 1 } template, err := r.queries.CreateCollectionTemplate(ctx, sqlite.CreateCollectionTemplateParams{ CollectionID: collectionID, SiteID: siteID, Name: name, HtmlTemplate: htmlTemplate, IsDefault: isDefaultInt, }) if err != nil { return nil, err } return &CollectionTemplateItem{ TemplateID: int(template.TemplateID), CollectionID: template.CollectionID, SiteID: template.SiteID, Name: template.Name, HTMLTemplate: template.HtmlTemplate, IsDefault: template.IsDefault != 0, }, nil } // GetCollectionTemplates retrieves all templates for a collection func (r *SQLiteRepository) GetCollectionTemplates(ctx context.Context, siteID, collectionID string) ([]CollectionTemplateItem, error) { templates, err := r.queries.GetCollectionTemplates(ctx, sqlite.GetCollectionTemplatesParams{ CollectionID: collectionID, SiteID: siteID, }) if err != nil { return nil, err } result := make([]CollectionTemplateItem, len(templates)) for i, template := range templates { result[i] = CollectionTemplateItem{ TemplateID: int(template.TemplateID), CollectionID: template.CollectionID, SiteID: template.SiteID, Name: template.Name, HTMLTemplate: template.HtmlTemplate, IsDefault: template.IsDefault != 0, // SQLite uses INTEGER for boolean } } return result, nil } // GetCollectionTemplate retrieves a single template by ID func (r *SQLiteRepository) GetCollectionTemplate(ctx context.Context, templateID int) (*CollectionTemplateItem, error) { template, err := r.queries.GetCollectionTemplate(ctx, int64(templateID)) if err != nil { return nil, err } result := &CollectionTemplateItem{ TemplateID: int(template.TemplateID), CollectionID: template.CollectionID, SiteID: template.SiteID, Name: template.Name, HTMLTemplate: template.HtmlTemplate, IsDefault: template.IsDefault != 0, // SQLite uses INTEGER for boolean } return result, nil } // CreateCollectionItem creates a new collection item func (r *SQLiteRepository) CreateCollectionItem(ctx context.Context, siteID, collectionID, itemID string, templateID int, htmlContent string, position int, lastEditedBy string) (*CollectionItemWithTemplate, error) { item, err := r.queries.CreateCollectionItem(ctx, sqlite.CreateCollectionItemParams{ ItemID: itemID, CollectionID: collectionID, SiteID: siteID, TemplateID: int64(templateID), HtmlContent: htmlContent, Position: int64(position), LastEditedBy: lastEditedBy, }) if err != nil { return nil, err } return &CollectionItemWithTemplate{ ItemID: item.ItemID, CollectionID: item.CollectionID, SiteID: item.SiteID, TemplateID: int(item.TemplateID), HTMLContent: item.HtmlContent, Position: int(item.Position), LastEditedBy: item.LastEditedBy, }, nil } // CreateCollectionItemAtomic creates a collection item with all its content entries atomically func (r *SQLiteRepository) CreateCollectionItemAtomic(ctx context.Context, siteID, collectionID string, templateID int, lastEditedBy string) (*CollectionItemWithTemplate, error) { // Get template HTML for processing templates, err := r.GetCollectionTemplates(ctx, siteID, collectionID) if err != nil { return nil, fmt.Errorf("failed to get templates: %w", err) } var templateHTML string for _, template := range templates { if template.TemplateID == templateID { templateHTML = template.HTMLTemplate break } } if templateHTML == "" { return nil, fmt.Errorf("template %d not found", templateID) } // TODO: Implement using unified engine approach // This requires circular dependency resolution return nil, fmt.Errorf("CreateCollectionItemAtomic not yet implemented for SQLite") } // GetMaxPosition returns the maximum position for items in a collection func (r *SQLiteRepository) GetMaxPosition(ctx context.Context, siteID, collectionID string) (int, error) { result, err := r.queries.GetMaxPosition(ctx, sqlite.GetMaxPositionParams{ CollectionID: collectionID, SiteID: siteID, }) if err != nil { return 0, err } // Convert interface{} to int (SQLite returns int64) if maxPos, ok := result.(int64); ok { return int(maxPos), nil } return 0, nil } // UpdateContent updates an existing content item func (r *SQLiteRepository) UpdateContent(ctx context.Context, siteID, contentID, htmlContent, lastEditedBy string) (*ContentItem, error) { content, err := r.queries.UpdateContent(ctx, sqlite.UpdateContentParams{ HtmlContent: htmlContent, LastEditedBy: lastEditedBy, ID: contentID, SiteID: siteID, }) if err != nil { return nil, err } return &ContentItem{ ID: content.ID, SiteID: content.SiteID, HTMLContent: content.HtmlContent, OriginalTemplate: FromNullString(content.OriginalTemplate), UpdatedAt: fmt.Sprintf("%d", content.UpdatedAt), LastEditedBy: content.LastEditedBy, }, nil } // ReorderCollectionItems reorders collection items in bulk func (r *SQLiteRepository) ReorderCollectionItems(ctx context.Context, siteID, collectionID string, items []CollectionItemPosition, lastEditedBy string) error { // Use transaction for atomic bulk updates tx, err := r.db.BeginTx(ctx, nil) if err != nil { return err } defer tx.Rollback() qtx := r.queries.WithTx(tx) for _, item := range items { err = qtx.UpdateCollectionItemPosition(ctx, sqlite.UpdateCollectionItemPositionParams{ ItemID: item.ItemID, CollectionID: collectionID, SiteID: siteID, Position: int64(item.Position), LastEditedBy: lastEditedBy, }) if err != nil { return fmt.Errorf("failed to update position for item %s: %w", item.ItemID, err) } } return tx.Commit() } // WithTransaction executes a function within a database transaction func (r *SQLiteRepository) WithTransaction(ctx context.Context, fn func(ContentRepository) error) error { tx, err := r.db.BeginTx(ctx, nil) if err != nil { return err } txRepo := &SQLiteRepository{ queries: r.queries.WithTx(tx), db: r.db, } if err := fn(txRepo); err != nil { tx.Rollback() return err } return tx.Commit() }