// File Path: monorepo/cloud/maplepress-backend/internal/repo/page_repo.go
package repo

import (
	"context"
	"fmt"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/page"
)

type pageRepository struct {
	session *gocql.Session
	logger  *zap.Logger
}

func NewPageRepository(session *gocql.Session, logger *zap.Logger) page.Repository {
	return &pageRepository{
		session: session,
		logger:  logger.Named("page-repo"),
	}
}

// Create inserts a new page
func (r *pageRepository) Create(ctx context.Context, p *page.Page) error {
	query := `
		INSERT INTO maplepress.pages_by_site (
			site_id, page_id, tenant_id,
			title, content, excerpt, url,
			status, post_type, author,
			published_at, modified_at, indexed_at,
			meilisearch_doc_id,
			created_at, updated_at,
			created_from_ip_address, created_from_ip_timestamp,
			modified_from_ip_address, modified_from_ip_timestamp
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
	`

	return r.session.Query(query,
		p.SiteID, p.PageID, p.TenantID,
		p.Title, p.Content, p.Excerpt, p.URL,
		p.Status, p.PostType, p.Author,
		p.PublishedAt, p.ModifiedAt, p.IndexedAt,
		p.MeilisearchDocID,
		p.CreatedAt, p.UpdatedAt,
		p.CreatedFromIPAddress, p.CreatedFromIPTimestamp,
		p.ModifiedFromIPAddress, p.ModifiedFromIPTimestamp,
	).WithContext(ctx).Exec()
}

// Update updates an existing page
func (r *pageRepository) Update(ctx context.Context, p *page.Page) error {
	query := `
		UPDATE maplepress.pages_by_site SET
			title = ?,
			content = ?,
			excerpt = ?,
			url = ?,
			status = ?,
			post_type = ?,
			author = ?,
			published_at = ?,
			modified_at = ?,
			indexed_at = ?,
			meilisearch_doc_id = ?,
			updated_at = ?,
			modified_from_ip_address = ?,
			modified_from_ip_timestamp = ?
		WHERE site_id = ? AND page_id = ?
	`

	return r.session.Query(query,
		p.Title, p.Content, p.Excerpt, p.URL,
		p.Status, p.PostType, p.Author,
		p.PublishedAt, p.ModifiedAt, p.IndexedAt,
		p.MeilisearchDocID,
		p.UpdatedAt,
		p.ModifiedFromIPAddress, p.ModifiedFromIPTimestamp,
		p.SiteID, p.PageID,
	).WithContext(ctx).Exec()
}

// Upsert creates or updates a page
func (r *pageRepository) Upsert(ctx context.Context, p *page.Page) error {
	// In Cassandra, INSERT acts as an upsert
	return r.Create(ctx, p)
}

// GetByID retrieves a page by site_id and page_id
func (r *pageRepository) GetByID(ctx context.Context, siteID gocql.UUID, pageID string) (*page.Page, error) {
	query := `
		SELECT site_id, page_id, tenant_id,
			title, content, excerpt, url,
			status, post_type, author,
			published_at, modified_at, indexed_at,
			meilisearch_doc_id,
			created_at, updated_at,
			created_from_ip_address, created_from_ip_timestamp,
			modified_from_ip_address, modified_from_ip_timestamp
		FROM maplepress.pages_by_site
		WHERE site_id = ? AND page_id = ?
	`

	p := &page.Page{}
	err := r.session.Query(query, siteID, pageID).
		WithContext(ctx).
		Scan(
			&p.SiteID, &p.PageID, &p.TenantID,
			&p.Title, &p.Content, &p.Excerpt, &p.URL,
			&p.Status, &p.PostType, &p.Author,
			&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
			&p.MeilisearchDocID,
			&p.CreatedAt, &p.UpdatedAt,
			&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
			&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
		)

	if err == gocql.ErrNotFound {
		return nil, fmt.Errorf("page not found: site_id=%s, page_id=%s", siteID, pageID)
	}

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

	return p, nil
}

// GetBySiteID retrieves all pages for a site
func (r *pageRepository) GetBySiteID(ctx context.Context, siteID gocql.UUID) ([]*page.Page, error) {
	query := `
		SELECT site_id, page_id, tenant_id,
			title, content, excerpt, url,
			status, post_type, author,
			published_at, modified_at, indexed_at,
			meilisearch_doc_id,
			created_at, updated_at,
			created_from_ip_address, created_from_ip_timestamp,
			modified_from_ip_address, modified_from_ip_timestamp
		FROM maplepress.pages_by_site
		WHERE site_id = ?
	`

	iter := r.session.Query(query, siteID).WithContext(ctx).Iter()
	defer iter.Close()

	var pages []*page.Page
	p := &page.Page{}

	for iter.Scan(
		&p.SiteID, &p.PageID, &p.TenantID,
		&p.Title, &p.Content, &p.Excerpt, &p.URL,
		&p.Status, &p.PostType, &p.Author,
		&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
		&p.MeilisearchDocID,
		&p.CreatedAt, &p.UpdatedAt,
		&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
		&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
	) {
		pages = append(pages, p)
		p = &page.Page{} // Create new instance for next iteration
	}

	if err := iter.Close(); err != nil {
		return nil, fmt.Errorf("failed to iterate pages: %w", err)
	}

	return pages, nil
}

// GetBySiteIDPaginated retrieves pages for a site with pagination
func (r *pageRepository) GetBySiteIDPaginated(ctx context.Context, siteID gocql.UUID, limit int, pageState []byte) ([]*page.Page, []byte, error) {
	query := `
		SELECT site_id, page_id, tenant_id,
			title, content, excerpt, url,
			status, post_type, author,
			published_at, modified_at, indexed_at,
			meilisearch_doc_id,
			created_at, updated_at,
			created_from_ip_address, created_from_ip_timestamp,
			modified_from_ip_address, modified_from_ip_timestamp
		FROM maplepress.pages_by_site
		WHERE site_id = ?
	`

	q := r.session.Query(query, siteID).WithContext(ctx).PageSize(limit)

	if len(pageState) > 0 {
		q = q.PageState(pageState)
	}

	iter := q.Iter()
	defer iter.Close()

	var pages []*page.Page
	p := &page.Page{}

	for iter.Scan(
		&p.SiteID, &p.PageID, &p.TenantID,
		&p.Title, &p.Content, &p.Excerpt, &p.URL,
		&p.Status, &p.PostType, &p.Author,
		&p.PublishedAt, &p.ModifiedAt, &p.IndexedAt,
		&p.MeilisearchDocID,
		&p.CreatedAt, &p.UpdatedAt,
		&p.CreatedFromIPAddress, &p.CreatedFromIPTimestamp,
		&p.ModifiedFromIPAddress, &p.ModifiedFromIPTimestamp,
	) {
		pages = append(pages, p)
		p = &page.Page{} // Create new instance for next iteration
	}

	if err := iter.Close(); err != nil {
		return nil, nil, fmt.Errorf("failed to iterate pages: %w", err)
	}

	nextPageState := iter.PageState()
	return pages, nextPageState, nil
}

// Delete deletes a page
func (r *pageRepository) Delete(ctx context.Context, siteID gocql.UUID, pageID string) error {
	query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`
	return r.session.Query(query, siteID, pageID).WithContext(ctx).Exec()
}

// DeleteBySiteID deletes all pages for a site
func (r *pageRepository) DeleteBySiteID(ctx context.Context, siteID gocql.UUID) error {
	// Note: This is an expensive operation in Cassandra
	// Better to delete partition by partition if possible
	query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ?`
	return r.session.Query(query, siteID).WithContext(ctx).Exec()
}

// DeleteMultiple deletes multiple pages by their IDs
func (r *pageRepository) DeleteMultiple(ctx context.Context, siteID gocql.UUID, pageIDs []string) error {
	// Use batch for efficiency
	batch := r.session.NewBatch(gocql.LoggedBatch).WithContext(ctx)

	query := `DELETE FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`

	for _, pageID := range pageIDs {
		batch.Query(query, siteID, pageID)
	}

	return r.session.ExecuteBatch(batch)
}

// CountBySiteID counts pages for a site
func (r *pageRepository) CountBySiteID(ctx context.Context, siteID gocql.UUID) (int64, error) {
	query := `SELECT COUNT(*) FROM maplepress.pages_by_site WHERE site_id = ?`

	var count int64
	err := r.session.Query(query, siteID).WithContext(ctx).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("failed to count pages: %w", err)
	}

	return count, nil
}

// Exists checks if a page exists
func (r *pageRepository) Exists(ctx context.Context, siteID gocql.UUID, pageID string) (bool, error) {
	query := `SELECT page_id FROM maplepress.pages_by_site WHERE site_id = ? AND page_id = ?`

	var id string
	err := r.session.Query(query, siteID, pageID).WithContext(ctx).Scan(&id)

	if err == gocql.ErrNotFound {
		return false, nil
	}

	if err != nil {
		return false, fmt.Errorf("failed to check page existence: %w", err)
	}

	return true, nil
}
