package db

import (
	"context"

	"github.com/dtoro/oikos/internal/adapters/postgres/sqlcgen"
	"github.com/dtoro/oikos/internal/core/domain"
	"github.com/dtoro/oikos/internal/core/ports"
	"github.com/google/uuid"
)

// MetricsRepo implements ports.MetricsRepository.
type MetricsRepo struct {
	pool *Pool
}

var _ ports.MetricsRepository = (*MetricsRepo)(nil)

func NewMetricsRepo(pool *Pool) *MetricsRepo { return &MetricsRepo{pool: pool} }

func (m *MetricsRepo) InsertSamples(ctx context.Context, entityID domain.UUID, samples []ports.MetricSample) error {
	for _, s := range samples {
		if err := sqlcgen.New(m.pool).InsertMetricSample(ctx, sqlcgen.InsertMetricSampleParams{
			EntityID: mustUUID(entityID), Metric: s.Metric, Value: s.Value,
		}); err != nil {
			return err
		}
	}
	return nil
}

// SignalRepo implements ports.SignalRepository with inline SQL.
type SignalRepo struct {
	pool *Pool
}

var _ ports.SignalRepository = (*SignalRepo)(nil)

func NewSignalRepo(pool *Pool) *SignalRepo { return &SignalRepo{pool: pool} }

func (r *SignalRepo) Open(ctx context.Context) ([]domain.Signal, error) {
	rows, err := r.pool.Query(ctx,
		`SELECT entity_id, kind, severity, state FROM signals WHERE state NOT IN ('resolved','failed') ORDER BY severity DESC`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	var items []domain.Signal
	for rows.Next() {
		var s domain.Signal
		var id uuid.UUID
		if err := rows.Scan(&id, &s.Kind, &s.Severity, &s.State); err != nil {
			return nil, err
		}
		s.EntityID = domain.UUID(id.String())
		items = append(items, s)
	}
	return items, rows.Err()
}

func (r *SignalRepo) History(ctx context.Context, entityID domain.UUID, limit int) ([]domain.Signal, error) {
	return nil, nil
}

func (r *SignalRepo) UpsertWithTriggers(ctx context.Context, input ports.SignalUpsertInput) error {
	eid := mustUUID(input.Signal.EntityID)
	_, err := r.pool.Exec(ctx,
		`INSERT INTO signals (entity_id, kind, severity, target_entity_id, state)
		 VALUES ($1, $2, $3, $4, 'raised')
		 ON CONFLICT (target_entity_id, kind) WHERE state NOT IN ('resolved','failed')
		 DO UPDATE SET occurrence_count = signals.occurrence_count + 1,
		               last_seen_at = now(), updated_at = now()`,
		eid, input.Signal.Kind, input.Signal.Severity, eid)
	return err
}

func (r *SignalRepo) Transition(ctx context.Context, input ports.SignalTransitionInput) (domain.Signal, error) {
	tag, err := r.pool.Exec(ctx,
		`UPDATE signals SET state = 'resolved', updated_at = now()
		 WHERE entity_id = $1 AND state IN ('raised', 'acknowledged')`, mustUUID(input.SignalID))
	if err != nil {
		return domain.Signal{}, err
	}
	if tag.RowsAffected() == 0 {
		return domain.Signal{}, domain.ErrNotFound
	}
	return domain.Signal{}, nil
}