package sqlite

import (
	"context"
	"fmt"
	"strings"
)

type RouteFailoverEvent struct {
	ID             int64
	RequestID      string
	LogicalGroupID string
	PublicModel    string
	FromRouteID    string
	ToRouteID      string
	Reason         string
	FailureCount   int
	CreatedAt      string
}

type RouteFailoverEventFilter struct {
	RequestID      string
	LogicalGroupID string
	PublicModel    string
	FromRouteID    string
	ToRouteID      string
	Limit          int
}

type RouteFailoverEventsRepo struct {
	db execQuerier
}

func newRouteFailoverEventsRepo(db execQuerier) *RouteFailoverEventsRepo {
	return &RouteFailoverEventsRepo{db: db}
}

func (r *RouteFailoverEventsRepo) Create(ctx context.Context, row RouteFailoverEvent) (int64, error) {
	row, err := normalizeRouteFailoverEvent(row)
	if err != nil {
		return 0, err
	}

	result, err := r.db.ExecContext(
		ctx,
		`INSERT INTO route_failover_events (
			request_id,
			logical_group_id,
			public_model,
			from_route_id,
			to_route_id,
			reason,
			failure_count
		) VALUES (?, ?, ?, ?, ?, ?, ?)`,
		row.RequestID,
		row.LogicalGroupID,
		row.PublicModel,
		row.FromRouteID,
		row.ToRouteID,
		row.Reason,
		row.FailureCount,
	)
	if err != nil {
		return 0, fmt.Errorf("insert route failover event %q: %w", row.RequestID, err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return 0, fmt.Errorf("read inserted route failover event id for %q: %w", row.RequestID, err)
	}
	return id, nil
}

func (r *RouteFailoverEventsRepo) ListRecent(ctx context.Context, filter RouteFailoverEventFilter) ([]RouteFailoverEvent, error) {
	clauses := make([]string, 0, 5)
	args := make([]any, 0, 6)

	if requestID := strings.TrimSpace(filter.RequestID); requestID != "" {
		clauses = append(clauses, "request_id = ?")
		args = append(args, requestID)
	}
	if logicalGroupID := strings.TrimSpace(filter.LogicalGroupID); logicalGroupID != "" {
		clauses = append(clauses, "logical_group_id = ?")
		args = append(args, logicalGroupID)
	}
	if publicModel := strings.TrimSpace(filter.PublicModel); publicModel != "" {
		clauses = append(clauses, "public_model = ?")
		args = append(args, publicModel)
	}
	if fromRouteID := strings.TrimSpace(filter.FromRouteID); fromRouteID != "" {
		clauses = append(clauses, "from_route_id = ?")
		args = append(args, fromRouteID)
	}
	if toRouteID := strings.TrimSpace(filter.ToRouteID); toRouteID != "" {
		clauses = append(clauses, "to_route_id = ?")
		args = append(args, toRouteID)
	}

	query := `SELECT id, request_id, logical_group_id, public_model, from_route_id, to_route_id, reason, failure_count, created_at
		FROM route_failover_events`
	if len(clauses) > 0 {
		query += " WHERE " + strings.Join(clauses, " AND ")
	}
	query += " ORDER BY id DESC LIMIT ?"
	args = append(args, normalizeRouteLogListLimit(filter.Limit))

	rows, err := r.db.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("list route failover events: %w", err)
	}
	defer rows.Close()

	items := make([]RouteFailoverEvent, 0)
	for rows.Next() {
		var item RouteFailoverEvent
		if err := rows.Scan(
			&item.ID,
			&item.RequestID,
			&item.LogicalGroupID,
			&item.PublicModel,
			&item.FromRouteID,
			&item.ToRouteID,
			&item.Reason,
			&item.FailureCount,
			&item.CreatedAt,
		); err != nil {
			return nil, fmt.Errorf("scan route failover event: %w", err)
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("iterate route failover events: %w", err)
	}
	return items, nil
}

func normalizeRouteFailoverEvent(row RouteFailoverEvent) (RouteFailoverEvent, error) {
	row.RequestID = strings.TrimSpace(row.RequestID)
	row.LogicalGroupID = strings.TrimSpace(row.LogicalGroupID)
	row.PublicModel = strings.TrimSpace(row.PublicModel)
	row.FromRouteID = strings.TrimSpace(row.FromRouteID)
	row.ToRouteID = strings.TrimSpace(row.ToRouteID)
	row.Reason = strings.TrimSpace(row.Reason)

	switch {
	case row.RequestID == "":
		return RouteFailoverEvent{}, fmt.Errorf("request_id is required")
	case row.LogicalGroupID == "":
		return RouteFailoverEvent{}, fmt.Errorf("logical_group_id is required")
	case row.PublicModel == "":
		return RouteFailoverEvent{}, fmt.Errorf("public_model is required")
	case row.FromRouteID == "":
		return RouteFailoverEvent{}, fmt.Errorf("from_route_id is required")
	case row.ToRouteID == "":
		return RouteFailoverEvent{}, fmt.Errorf("to_route_id is required")
	case row.Reason == "":
		return RouteFailoverEvent{}, fmt.Errorf("reason is required")
	case row.FailureCount < 0:
		return RouteFailoverEvent{}, fmt.Errorf("failure_count must be >= 0")
	}

	return row, nil
}
