package postgres

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

	"chat-switchboard/models"
)

// ── SessionStore ───────────────────────────

type SessionStore struct{}

func NewSessionStore() *SessionStore { return &SessionStore{} }

func (s *SessionStore) Create(ctx context.Context, sp *models.SessionParticipant) error {
	return DB.QueryRowContext(ctx, `
		INSERT INTO session_participants (session_token, channel_id, display_name, fingerprint)
		VALUES ($1, $2, $3, $4)
		RETURNING id, created_at`,
		sp.SessionToken, sp.ChannelID, sp.DisplayName, nullText(sp.Fingerprint),
	).Scan(&sp.ID, &sp.CreatedAt)
}

func (s *SessionStore) GetByToken(ctx context.Context, token string) (*models.SessionParticipant, error) {
	sp := &models.SessionParticipant{}
	err := DB.QueryRowContext(ctx, `
		SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
		FROM session_participants WHERE session_token = $1`, token,
	).Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt)
	if err != nil {
		return nil, err
	}
	return sp, nil
}

func (s *SessionStore) GetByID(ctx context.Context, id string) (*models.SessionParticipant, error) {
	sp := &models.SessionParticipant{}
	err := DB.QueryRowContext(ctx, `
		SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
		FROM session_participants WHERE id = $1`, id,
	).Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt)
	if err != nil {
		return nil, err
	}
	return sp, nil
}

func (s *SessionStore) ListForChannel(ctx context.Context, channelID string) ([]models.SessionParticipant, error) {
	rows, err := DB.QueryContext(ctx, `
		SELECT id, session_token, channel_id, display_name, COALESCE(fingerprint, ''), created_at
		FROM session_participants WHERE channel_id = $1
		ORDER BY created_at ASC`, channelID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var result []models.SessionParticipant
	for rows.Next() {
		var sp models.SessionParticipant
		if err := rows.Scan(&sp.ID, &sp.SessionToken, &sp.ChannelID, &sp.DisplayName, &sp.Fingerprint, &sp.CreatedAt); err != nil {
			return nil, err
		}
		result = append(result, sp)
	}
	return result, rows.Err()
}

func (s *SessionStore) CountForChannel(ctx context.Context, channelID string) (int, error) {
	var count int
	err := DB.QueryRowContext(ctx, `
		SELECT COUNT(*) FROM session_participants WHERE channel_id = $1`, channelID,
	).Scan(&count)
	return count, err
}

func (s *SessionStore) Delete(ctx context.Context, id string) error {
	res, err := DB.ExecContext(ctx, `DELETE FROM session_participants WHERE id = $1`, id)
	if err != nil {
		return err
	}
	n, _ := res.RowsAffected()
	if n == 0 {
		return sql.ErrNoRows
	}
	return nil
}

// DeleteExpired removes sessions created before olderThan whose channel
// has no messages. Returns the count of deleted rows.
func (s *SessionStore) DeleteExpired(ctx context.Context, olderThan time.Time) (int64, error) {
	res, err := DB.ExecContext(ctx, `
		DELETE FROM session_participants sp
		WHERE sp.created_at < $1
		AND NOT EXISTS (
			SELECT 1 FROM messages m WHERE m.channel_id = sp.channel_id
		)`, olderThan)
	if err != nil {
		return 0, err
	}
	return res.RowsAffected()
}

// nullText returns nil for empty strings.
func nullText(s string) interface{} {
	if s == "" {
		return nil
	}
	return s
}
