package routing

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"go.uber.org/zap"
)

// PgStore implements RuleStore using PostgreSQL (jackc/pgx/v5 via database/sql).
type PgStore struct {
	db     *sql.DB
	logger *zap.Logger
}

// NewPgStore creates a PgStore backed by the given database connection.
func NewPgStore(db *sql.DB, logger *zap.Logger) *PgStore {
	return &PgStore{db: db, logger: logger}
}

func (p *PgStore) ListActive(ctx context.Context, tenantID string) ([]RoutingRule, error) {
	const q = `
		SELECT id, tenant_id, name, COALESCE(description,''), conditions, action, priority, is_enabled, created_at, updated_at
		FROM routing_rules
		WHERE tenant_id = $1 AND is_enabled = TRUE
		ORDER BY priority ASC`

	rows, err := p.db.QueryContext(ctx, q, tenantID)
	if err != nil {
		return nil, fmt.Errorf("routing_rules list: %w", err)
	}
	defer rows.Close() //nolint:errcheck

	var rules []RoutingRule
	for rows.Next() {
		r, err := scanRule(rows)
		if err != nil {
			return nil, err
		}
		rules = append(rules, r)
	}
	return rules, rows.Err()
}

func (p *PgStore) Get(ctx context.Context, id, tenantID string) (RoutingRule, error) {
	const q = `
		SELECT id, tenant_id, name, COALESCE(description,''), conditions, action, priority, is_enabled, created_at, updated_at
		FROM routing_rules
		WHERE id = $1 AND tenant_id = $2`

	row := p.db.QueryRowContext(ctx, q, id, tenantID)
	r, err := scanRule(row)
	if errors.Is(err, sql.ErrNoRows) {
		return RoutingRule{}, ErrNotFound
	}
	return r, err
}

func (p *PgStore) Create(ctx context.Context, rule RoutingRule) (RoutingRule, error) {
	condJSON, err := json.Marshal(rule.Conditions)
	if err != nil {
		return RoutingRule{}, fmt.Errorf("marshal conditions: %w", err)
	}
	actionJSON, err := json.Marshal(rule.Action)
	if err != nil {
		return RoutingRule{}, fmt.Errorf("marshal action: %w", err)
	}

	const q = `
		INSERT INTO routing_rules (tenant_id, name, description, conditions, action, priority, is_enabled)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		RETURNING id, tenant_id, name, COALESCE(description,''), conditions, action, priority, is_enabled, created_at, updated_at`

	row := p.db.QueryRowContext(ctx, q,
		rule.TenantID, rule.Name, rule.Description,
		condJSON, actionJSON, rule.Priority, rule.IsEnabled,
	)
	return scanRule(row)
}

func (p *PgStore) Update(ctx context.Context, rule RoutingRule) (RoutingRule, error) {
	condJSON, err := json.Marshal(rule.Conditions)
	if err != nil {
		return RoutingRule{}, fmt.Errorf("marshal conditions: %w", err)
	}
	actionJSON, err := json.Marshal(rule.Action)
	if err != nil {
		return RoutingRule{}, fmt.Errorf("marshal action: %w", err)
	}

	const q = `
		UPDATE routing_rules
		SET name=$3, description=$4, conditions=$5, action=$6, priority=$7, is_enabled=$8
		WHERE id=$1 AND tenant_id=$2
		RETURNING id, tenant_id, name, COALESCE(description,''), conditions, action, priority, is_enabled, created_at, updated_at`

	row := p.db.QueryRowContext(ctx, q,
		rule.ID, rule.TenantID, rule.Name, rule.Description,
		condJSON, actionJSON, rule.Priority, rule.IsEnabled,
	)
	r, err := scanRule(row)
	if errors.Is(err, sql.ErrNoRows) {
		return RoutingRule{}, ErrNotFound
	}
	return r, err
}

func (p *PgStore) Delete(ctx context.Context, id, tenantID string) error {
	const q = `DELETE FROM routing_rules WHERE id = $1 AND tenant_id = $2`
	res, err := p.db.ExecContext(ctx, q, id, tenantID)
	if err != nil {
		return fmt.Errorf("routing_rules delete: %w", err)
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return ErrNotFound
	}
	return nil
}

// ─── scanner ─────────────────────────────────────────────────────────────────

// scanner is satisfied by both *sql.Row and *sql.Rows.
type scanner interface {
	Scan(dest ...interface{}) error
}

func scanRule(s scanner) (RoutingRule, error) {
	var (
		r          RoutingRule
		condJSON   []byte
		actionJSON []byte
		createdAt  time.Time
		updatedAt  time.Time
	)
	err := s.Scan(
		&r.ID, &r.TenantID, &r.Name, &r.Description,
		&condJSON, &actionJSON,
		&r.Priority, &r.IsEnabled,
		&createdAt, &updatedAt,
	)
	if err != nil {
		return RoutingRule{}, fmt.Errorf("scanning routing_rule row: %w", err)
	}
	r.CreatedAt = createdAt
	r.UpdatedAt = updatedAt

	if err := json.Unmarshal(condJSON, &r.Conditions); err != nil {
		return RoutingRule{}, fmt.Errorf("parsing conditions JSON: %w", err)
	}
	if err := json.Unmarshal(actionJSON, &r.Action); err != nil {
		return RoutingRule{}, fmt.Errorf("parsing action JSON: %w", err)
	}
	return r, nil
}
