package postgres

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

	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/port"
)

// BuildEventRepository implements port.BuildEventStore using PostgreSQL.
type BuildEventRepository struct {
	db *sql.DB
}

// NewBuildEventRepository creates a new build event repository.
func NewBuildEventRepository(db *sql.DB) *BuildEventRepository {
	return &BuildEventRepository{db: db}
}

// Ensure BuildEventRepository implements port.BuildEventStore at compile time.
var _ port.BuildEventStore = (*BuildEventRepository)(nil)

// buildEventRow is the database representation of a build event.
type buildEventRow struct {
	ID        string    `db:"id"`
	TaskID    string    `db:"task_id"`
	ProjectID string    `db:"project_id"`
	Type      string    `db:"type"`
	Sequence  int64     `db:"sequence"`
	Timestamp time.Time `db:"timestamp"`
	Data      []byte    `db:"data"`
	CreatedAt time.Time `db:"created_at"`
}

// Record stores a build event for later replay.
func (r *BuildEventRepository) Record(ctx context.Context, event *domain.BuildEvent) error {
	dataBytes, err := json.Marshal(event.Data)
	if err != nil {
		return err
	}

	_, err = r.db.ExecContext(ctx, `
		INSERT INTO build_events (id, task_id, project_id, type, sequence, timestamp, data)
		VALUES ($1, $2, $3, $4, $5, $6, $7)
		ON CONFLICT (id) DO NOTHING
	`, event.ID, event.TaskID, event.ProjectID, event.Type, event.Sequence, event.Timestamp, dataBytes)

	return err
}

// ListByTask retrieves events for a task, optionally after a sequence number.
func (r *BuildEventRepository) ListByTask(ctx context.Context, taskID string, afterSequence int64) ([]*domain.BuildEvent, error) {
	rows, err := r.db.QueryContext(ctx, `
		SELECT id, task_id, project_id, type, sequence, timestamp, data
		FROM build_events
		WHERE task_id = $1 AND sequence > $2
		ORDER BY sequence ASC
	`, taskID, afterSequence)
	if err != nil {
		return nil, err
	}
	defer func() { _ = rows.Close() }()

	var events []*domain.BuildEvent
	for rows.Next() {
		var row buildEventRow
		if err := rows.Scan(&row.ID, &row.TaskID, &row.ProjectID, &row.Type, &row.Sequence, &row.Timestamp, &row.Data); err != nil {
			return nil, err
		}

		var data domain.BuildEventData
		if err := json.Unmarshal(row.Data, &data); err != nil {
			// If unmarshal fails, use empty data
			data = domain.BuildEventData{}
		}

		events = append(events, &domain.BuildEvent{
			ID:        row.ID,
			TaskID:    row.TaskID,
			ProjectID: row.ProjectID,
			Type:      domain.BuildEventType(row.Type),
			Sequence:  row.Sequence,
			Timestamp: row.Timestamp,
			Data:      data,
		})
	}

	return events, rows.Err()
}

// Cleanup removes events older than the specified age.
func (r *BuildEventRepository) Cleanup(ctx context.Context, olderThan time.Duration) (int64, error) {
	cutoff := time.Now().Add(-olderThan)

	result, err := r.db.ExecContext(ctx, `
		DELETE FROM build_events
		WHERE created_at < $1
	`, cutoff)
	if err != nil {
		return 0, err
	}

	return result.RowsAffected()
}
