package meta

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"math"
	"time"
	"unicode/utf8"
)

const (
	TenantEmbeddingAPIBaseMaxChars      = 512
	TenantEmbeddingAPIKeyCipherMaxBytes = 2048
	TenantEmbeddingModelMaxChars        = 255
)

// TenantEmbeddingConfig stores one tenant's app-managed embedding provider.
// Provider fields are empty when the row is an explicit disabled marker.
type TenantEmbeddingConfig struct {
	TenantID       string
	Enabled        bool
	APIBase        string
	APIKeyCipher   []byte
	Model          string
	Generation     uint64
	ConfigRevision uint64
	CreatedAt      time.Time
	UpdatedAt      time.Time
}

func validateTenantEmbeddingConfig(cfg *TenantEmbeddingConfig) error {
	if cfg == nil {
		return fmt.Errorf("nil tenant embedding config")
	}
	if cfg.TenantID == "" {
		return fmt.Errorf("tenant embedding config requires tenant id")
	}
	if cfg.Generation == 0 || cfg.ConfigRevision == 0 {
		return fmt.Errorf("tenant embedding config requires non-zero generation and config revision")
	}
	if cfg.Generation > math.MaxInt64 || cfg.ConfigRevision > math.MaxInt64 {
		return fmt.Errorf("tenant embedding config counters exceed signed bigint range")
	}
	if cfg.Enabled {
		if cfg.APIBase == "" || len(cfg.APIKeyCipher) == 0 || cfg.Model == "" {
			return fmt.Errorf("enabled tenant embedding config requires api base, api key and model")
		}
		if utf8.RuneCountInString(cfg.APIBase) > TenantEmbeddingAPIBaseMaxChars {
			return fmt.Errorf("tenant embedding api base exceeds %d characters", TenantEmbeddingAPIBaseMaxChars)
		}
		if len(cfg.APIKeyCipher) > TenantEmbeddingAPIKeyCipherMaxBytes {
			return fmt.Errorf("tenant embedding api key ciphertext exceeds %d bytes", TenantEmbeddingAPIKeyCipherMaxBytes)
		}
		if utf8.RuneCountInString(cfg.Model) > TenantEmbeddingModelMaxChars {
			return fmt.Errorf("tenant embedding model exceeds %d characters", TenantEmbeddingModelMaxChars)
		}
		return nil
	}
	if cfg.APIBase != "" || len(cfg.APIKeyCipher) != 0 || cfg.Model != "" {
		return fmt.Errorf("disabled tenant embedding config must not contain provider fields")
	}
	return nil
}

func (s *Store) UpsertTenantEmbeddingConfig(ctx context.Context, cfg *TenantEmbeddingConfig) (err error) {
	start := time.Now()
	defer observeMeta(ctx, "upsert_tenant_embedding_config", start, &err)
	return upsertTenantEmbeddingConfig(ctx, s.db, cfg)
}

func (s *Store) UpsertTenantEmbeddingConfigTx(ctx context.Context, tx *sql.Tx, cfg *TenantEmbeddingConfig) error {
	return upsertTenantEmbeddingConfig(ctx, tx, cfg)
}

func upsertTenantEmbeddingConfig(ctx context.Context, ex execContexter, cfg *TenantEmbeddingConfig) error {
	if err := validateTenantEmbeddingConfig(cfg); err != nil {
		return err
	}
	now := time.Now().UTC()
	createdAt := cfg.CreatedAt.UTC()
	if cfg.CreatedAt.IsZero() {
		createdAt = now
	}
	updatedAt := cfg.UpdatedAt.UTC()
	if cfg.UpdatedAt.IsZero() {
		updatedAt = now
	}
	_, err := ex.ExecContext(ctx, `INSERT INTO tenant_embedding_configs
		(tenant_id, enabled, api_base, api_key_cipher, model, generation, config_revision, created_at, updated_at)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			enabled = VALUES(enabled),
			api_base = VALUES(api_base),
			api_key_cipher = VALUES(api_key_cipher),
			model = VALUES(model),
			generation = VALUES(generation),
			config_revision = VALUES(config_revision),
			updated_at = VALUES(updated_at)`,
		cfg.TenantID, cfg.Enabled, nullStr(cfg.APIBase), nullableBytes(cfg.APIKeyCipher), nullStr(cfg.Model),
		cfg.Generation, cfg.ConfigRevision, createdAt, updatedAt)
	return err
}

const tenantEmbeddingConfigColumns = `tenant_id, enabled, api_base, api_key_cipher, model,
		generation, config_revision, created_at, updated_at`

type rowScanner interface {
	Scan(dest ...any) error
}

func scanTenantEmbeddingConfig(row rowScanner) (*TenantEmbeddingConfig, error) {
	var rec TenantEmbeddingConfig
	var apiBase, model sql.NullString
	if err := row.Scan(&rec.TenantID, &rec.Enabled, &apiBase, &rec.APIKeyCipher, &model,
		&rec.Generation, &rec.ConfigRevision, &rec.CreatedAt, &rec.UpdatedAt); err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			return nil, ErrNotFound
		}
		return nil, err
	}
	if apiBase.Valid {
		rec.APIBase = apiBase.String
	}
	if model.Valid {
		rec.Model = model.String
	}
	return &rec, nil
}

func (s *Store) GetTenantEmbeddingConfig(ctx context.Context, tenantID string) (out *TenantEmbeddingConfig, err error) {
	start := time.Now()
	defer observeMeta(ctx, "get_tenant_embedding_config", start, &err)
	return scanTenantEmbeddingConfig(s.db.QueryRowContext(ctx, `SELECT `+tenantEmbeddingConfigColumns+`
		FROM tenant_embedding_configs WHERE tenant_id = ?`, tenantID))
}

func (s *Store) GetTenantEmbeddingConfigForUpdateTx(ctx context.Context, tx *sql.Tx, tenantID string) (*TenantEmbeddingConfig, error) {
	return scanTenantEmbeddingConfig(tx.QueryRowContext(ctx, `SELECT `+tenantEmbeddingConfigColumns+`
		FROM tenant_embedding_configs WHERE tenant_id = ? FOR UPDATE`, tenantID))
}

func (s *Store) DeleteTenantEmbeddingConfig(ctx context.Context, tenantID string) (err error) {
	start := time.Now()
	defer observeMeta(ctx, "delete_tenant_embedding_config", start, &err)
	_, err = s.db.ExecContext(ctx, `DELETE FROM tenant_embedding_configs WHERE tenant_id = ?`, tenantID)
	return err
}

func deleteTenantEmbeddingConfigTx(ctx context.Context, tx *sql.Tx, tenantID string) error {
	_, err := tx.ExecContext(ctx, `DELETE FROM tenant_embedding_configs WHERE tenant_id = ?`, tenantID)
	return err
}
