package persistence

import (
	"context"
	"database/sql"
	"encoding/json"
	"strings"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/rs/zerolog/log"
)

// MetadataRepository handles database operations for platform metadata.
// Scanner functions are split across domain-specific files:
//   - metadata_scan_schema.go — object, field, and aliasing scanners
//   - metadata_scan_ui.go     — app, layout, list view, theme, and dashboard scanners
//   - metadata_scan_logic.go  — action and validation rule scanners
//   - repo_actions.go         — action CRUD
//   - repo_apps.go            — app CRUD
//   - repo_dashboards.go    — dashboard CRUD
//   - repo_fields.go          — field CRUD
//   - repo_listview.go        — list view CRUD
//   - repo_theme.go           — theme CRUD
//   - repo_ui.go              — compact layout and related list CRUD
//   - repo_validations.go     — validation rule CRUD
type MetadataRepository struct {
	db *sql.DB
}

func NewMetadataRepository(db *sql.DB) *MetadataRepository {
	return &MetadataRepository{db: db}
}

// Write Methods (Exec)

// Exec executes a query without returning result rows
func (r *MetadataRepository) Exec(ctx context.Context, query string, args ...interface{}) (sql.Result, error) {
	return r.db.ExecContext(ctx, query, args...)
}

func (r *MetadataRepository) marshalJSON(v interface{}) (string, error) {
	if v == nil {
		return "", nil
	}
	b, err := json.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (r *MetadataRepository) unmarshalJSON(data string, v interface{}) {
	if data == "" {
		return
	}
	if err := json.Unmarshal([]byte(data), v); err != nil {
		log.Error().Err(err).Str("data", data).Msg("Failed to unmarshal JSON")
	}
}

// Scan Helpers (Local to Package)

type Scannable interface {
	Scan(dest ...interface{}) error
}

func (r *MetadataRepository) GetExecutor(ctx context.Context) Executor {
	if tx, ok := ctx.Value(txContextKey{}).(*sql.Tx); ok && tx != nil {
		return tx
	}
	return r.db
}

func (r *MetadataRepository) getSchema(ctx context.Context, apiName string) (*models.ObjectMetadata, error) {
	cols := strings.Join(constants.ColsSystemObject, ", ")
	q := "SELECT " + cols + " FROM `_System_Object` WHERE `APIName` = ? AND `IsDeleted` = 0 LIMIT 1"
	row := r.GetExecutor(ctx).QueryRowContext(ctx, q, apiName)
	return r.scanObject(row)
}

// Schema loading leverages GetExecutor for proper transaction hygiene.
