package domain

import (
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/jackc/pgx/v5/pgtype"
)

// Valid Int64
type ValidInt64 struct {
	Value int64
	Valid bool
}

func (v ValidInt64) ToPG() pgtype.Int8 {
	return pgtype.Int8{
		Int64: v.Value,
		Valid: v.Valid,
	}
}

func (n *ValidInt64) UnmarshalJSON(data []byte) error {
	var s string
	if err := json.Unmarshal(data, &s); err == nil {
		if s == "" {
			n.Valid = false
			return nil
		}
		v, err := strconv.ParseInt(s, 10, 64)
		if err != nil {
			fmt.Printf("Failed to parse the value of %v \n\n", s)
			return err
		}
		n.Value, n.Valid = v, true
		return nil
	}

	var v int64
	if err := json.Unmarshal(data, &v); err == nil {
		n.Value, n.Valid = v, true
		return nil
	}
	fmt.Printf("Failed to parse the value of %v", s)
	return fmt.Errorf("invalid int64 value: %s", string(data))
}

func ConvertInt64Ptr(value *int64) ValidInt64 {
	if value == nil {
		return ValidInt64{}
	}
	return ValidInt64{
		Value: *value,
		Valid: true,
	}
}

// Valid Int
type ValidInt struct {
	Value int
	Valid bool
}

func (v ValidInt) ToPG() pgtype.Int4 {
	return pgtype.Int4{
		Int32: int32(v.Value),
		Valid: v.Valid,
	}
}
func ConvertIntPtr(value *int) ValidInt {
	if value == nil {
		return ValidInt{}
	}
	return ValidInt{
		Value: *value,
		Valid: true,
	}
}

// Valid Int32
type ValidInt32 struct {
	Value int32
	Valid bool
}

func (v ValidInt32) ToPG() pgtype.Int4 {
	return pgtype.Int4{
		Int32: v.Value,
		Valid: v.Valid,
	}
}

func ConvertInt32Ptr(value *int32) ValidInt32 {
	if value == nil {
		return ValidInt32{}
	}
	return ValidInt32{
		Value: *value,
		Valid: true,
	}
}

// Valid Float32
type ValidFloat32 struct {
	Value float32
	Valid bool
}

func (v ValidFloat32) ToPG() pgtype.Float4 {
	return pgtype.Float4{
		Float32: v.Value,
		Valid:   v.Valid,
	}
}

func ConvertFloat32Ptr(value *float32) ValidFloat32 {
	if value == nil {
		return ValidFloat32{}
	}
	return ValidFloat32{
		Value: *value,
		Valid: true,
	}
}

// Valid String
type ValidString struct {
	Value string
	Valid bool
}

func (v ValidString) ToPG() pgtype.Text {
	return pgtype.Text{
		String: v.Value,
		Valid:  v.Valid,
	}
}

func ConvertStringPtr(value *string) ValidString {
	if value == nil {
		return ValidString{}
	}
	return ValidString{
		Value: *value,
		Valid: true,
	}
}

// Valid Time
type ValidTime struct {
	Value time.Time
	Valid bool
}

func (v ValidTime) ToPG() pgtype.Timestamp {
	return pgtype.Timestamp{
		Time:  v.Value,
		Valid: v.Valid,
	}
}

// Valid Bool
type ValidBool struct {
	Value bool
	Valid bool
}

func (v ValidBool) ToPG() pgtype.Bool {
	return pgtype.Bool{
		Bool:  v.Value,
		Valid: v.Valid,
	}
}

func ConvertBoolPtr(value *bool) ValidBool {
	if value == nil {
		return ValidBool{}
	}
	return ValidBool{
		Value: *value,
		Valid: true,
	}
}

// Valid Currency
type ValidCurrency struct {
	Value Currency
	Valid bool
}

func (v ValidCurrency) ToPG() pgtype.Int8 {
	return pgtype.Int8{
		Int64: int64(v.Value),
		Valid: v.Valid,
	}
}

func ConvertCurrencyPtr(value *Currency) ValidCurrency {
	if value == nil {
		return ValidCurrency{}
	}

	return ValidCurrency{
		Value: *value,
		Valid: true,
	}
}

func ConvertFloat32PtrToCurrency(value *float32) ValidCurrency {
	if value == nil {
		return ValidCurrency{}
	}

	converted := ToCurrency(*value)

	return ValidCurrency{
		Value: converted,
		Valid: true,
	}
}

// Valid Role
type ValidRole struct {
	Value Role
	Valid bool
}

func (v ValidRole) ToPG() pgtype.Text {
	return pgtype.Text{
		String: string(v.Value),
		Valid:  v.Valid,
	}
}

func ConvertRolePtr(value *Role) ValidRole {
	if value == nil {
		return ValidRole{}
	}

	return ValidRole{
		Value: *value,
		Valid: true,
	}
}

func ConvertStringPtrToTime(value *string) (ValidTime, error) {
	if value == nil {
		return ValidTime{}, nil
	}

	parsedIntervalStart, err := time.Parse(time.RFC3339, *value)
	if err != nil {
		return ValidTime{}, nil
	}
	return ValidTime{
		Value: parsedIntervalStart,
		Valid: true,
	}, nil
}
