// Path: Backend/internal/handlers/api/shared/shared.go

package shared

import (
	"boostai-backend/internal/sqlc"
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/jackc/pgx/v5/pgtype"
	"golang.org/x/crypto/bcrypt"
)

const QueryTimeout = 5 * time.Second

type ListResponse[T any] struct {
	Data []T `json:"data"`
}

func WithTimeout() (context.Context, context.CancelFunc) {
	return context.WithTimeout(context.Background(), QueryTimeout)
}

func IsValidAnswerStatus(status string) bool {
	switch sqlc.AnswerStatus(strings.TrimSpace(status)) {
	case sqlc.AnswerStatusNotStarted,
		sqlc.AnswerStatusInProgress,
		sqlc.AnswerStatusSubmitted,
		sqlc.AnswerStatusReviewed:
		return true
	default:
		return false
	}
}

func NullableText(value *string) pgtype.Text {
	if value == nil {
		return pgtype.Text{}
	}

	trimmed := strings.TrimSpace(*value)
	if trimmed == "" {
		return pgtype.Text{}
	}

	return pgtype.Text{String: trimmed, Valid: true}
}

func MaybeHashPassword(value *string) (pgtype.Text, error) {
	if value == nil {
		return pgtype.Text{}, nil
	}

	trimmed := strings.TrimSpace(*value)
	if trimmed == "" {
		return pgtype.Text{}, nil
	}

	if len(trimmed) < 8 {
		return pgtype.Text{}, errors.New("password must be at least 8 characters")
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(trimmed), bcrypt.DefaultCost)
	if err != nil {
		return pgtype.Text{}, err
	}

	return pgtype.Text{String: string(hashedPassword), Valid: true}, nil
}

func NullableTime(value *time.Time) pgtype.Timestamptz {
	if value == nil {
		return pgtype.Timestamptz{}
	}

	return pgtype.Timestamptz{Time: value.UTC(), Valid: true}
}

func NullableBool(value *bool) pgtype.Bool {
	if value == nil {
		return pgtype.Bool{}
	}

	return pgtype.Bool{Bool: *value, Valid: true}
}

func TextPointer(value pgtype.Text) *string {
	if !value.Valid {
		return nil
	}

	text := value.String
	return &text
}

func TextValue(value pgtype.Text) string {
	if !value.Valid {
		return ""
	}

	return value.String
}

func TimePointer(value pgtype.Timestamptz) *time.Time {
	if !value.Valid {
		return nil
	}

	timestamp := value.Time.UTC()
	return &timestamp
}

func Int64Pointer(value pgtype.Int8) *int64 {
	if !value.Valid {
		return nil
	}

	v := value.Int64
	return &v
}

func BoolPointer(value pgtype.Bool) *bool {
	if !value.Valid {
		return nil
	}

	v := value.Bool
	return &v
}

func NullableFloat64AsNumeric(value *float64) (pgtype.Numeric, error) {
	if value == nil {
		return pgtype.Numeric{}, nil
	}

	numeric := pgtype.Numeric{}
	if err := numeric.ScanScientific(fmt.Sprintf("%f", *value)); err != nil {
		return pgtype.Numeric{}, err
	}

	return numeric, nil
}

func NumericPointer(value pgtype.Numeric) *float64 {
	if !value.Valid {
		return nil
	}

	floatValue, err := value.Float64Value()
	if err != nil || !floatValue.Valid {
		return nil
	}

	v := floatValue.Float64
	return &v
}
