package pgtype

import (
	
	
	
	
	
	

	
)

type DateScanner interface {
	ScanDate(v Date) error
}

type DateValuer interface {
	DateValue() (Date, error)
}

type Date struct {
	Time             time.Time
	InfinityModifier InfinityModifier
	Valid            bool
}

// ScanDate implements the [DateScanner] interface.
func ( *Date) ( Date) error {
	* = 
	return nil
}

// DateValue implements the [DateValuer] interface.
func ( Date) () (Date, error) {
	return , nil
}

const (
	negativeInfinityDayOffset = -2147483648
	infinityDayOffset         = 2147483647
)

// Scan implements the [database/sql.Scanner] interface.
func ( *Date) ( any) error {
	if  == nil {
		* = Date{}
		return nil
	}

	switch src := .(type) {
	case string:
		return scanPlanTextAnyToDateScanner{}.Scan([]byte(), )
	case time.Time:
		* = Date{Time: , Valid: true}
		return nil
	}

	return fmt.Errorf("cannot scan %T", )
}

// Value implements the [database/sql/driver.Valuer] interface.
func ( Date) () (driver.Value, error) {
	if !.Valid {
		return nil, nil
	}

	if .InfinityModifier != Finite {
		return .InfinityModifier.String(), nil
	}
	return .Time, nil
}

// MarshalJSON implements the [encoding/json.Marshaler] interface.
func ( Date) () ([]byte, error) {
	if !.Valid {
		return []byte("null"), nil
	}

	var  string

	switch .InfinityModifier {
	case Finite:
		 = .Time.Format("2006-01-02")
	case Infinity:
		 = "infinity"
	case NegativeInfinity:
		 = "-infinity"
	}

	return json.Marshal()
}

// UnmarshalJSON implements the [encoding/json.Unmarshaler] interface.
func ( *Date) ( []byte) error {
	var  *string
	 := json.Unmarshal(, &)
	if  != nil {
		return 
	}

	if  == nil {
		* = Date{}
		return nil
	}

	switch * {
	case "infinity":
		* = Date{Valid: true, InfinityModifier: Infinity}
	case "-infinity":
		* = Date{Valid: true, InfinityModifier: -Infinity}
	default:
		,  := time.ParseInLocation("2006-01-02", *, time.UTC)
		if  != nil {
			return 
		}

		* = Date{Time: , Valid: true}
	}

	return nil
}

type DateCodec struct{}

func (DateCodec) ( int16) bool {
	return  == TextFormatCode ||  == BinaryFormatCode
}

func (DateCodec) () int16 {
	return BinaryFormatCode
}

func (DateCodec) ( *Map,  uint32,  int16,  any) EncodePlan {
	if ,  := .(DateValuer); ! {
		return nil
	}

	switch  {
	case BinaryFormatCode:
		return encodePlanDateCodecBinary{}
	case TextFormatCode:
		return encodePlanDateCodecText{}
	}

	return nil
}

type encodePlanDateCodecBinary struct{}

func (encodePlanDateCodecBinary) ( any,  []byte) ( []byte,  error) {
	,  := .(DateValuer).DateValue()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	var  int32
	switch .InfinityModifier {
	case Finite:
		 := time.Date(.Time.Year(), .Time.Month(), .Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
		 := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()

		 :=  - 
		 = int32( / 86400)
	case Infinity:
		 = infinityDayOffset
	case NegativeInfinity:
		 = negativeInfinityDayOffset
	}

	return pgio.AppendInt32(, ), nil
}

type encodePlanDateCodecText struct{}

func (encodePlanDateCodecText) ( any,  []byte) ( []byte,  error) {
	,  := .(DateValuer).DateValue()
	if  != nil {
		return nil, 
	}

	if !.Valid {
		return nil, nil
	}

	switch .InfinityModifier {
	case Finite:
		// Year 0000 is 1 BC
		 := false
		 := .Time.Year()
		if  <= 0 {
			 = - + 1
			 = true
		}

		 := strconv.AppendInt(make([]byte, 0, 6), int64(), 10)
		for  := len();  < 4; ++ {
			 = append(, '0')
		}
		 = append(, ...)
		 = append(, '-')
		if .Time.Month() < 10 {
			 = append(, '0')
		}
		 = strconv.AppendInt(, int64(.Time.Month()), 10)
		 = append(, '-')
		if .Time.Day() < 10 {
			 = append(, '0')
		}
		 = strconv.AppendInt(, int64(.Time.Day()), 10)

		if  {
			 = append(, " BC"...)
		}
	case Infinity:
		 = append(, "infinity"...)
	case NegativeInfinity:
		 = append(, "-infinity"...)
	}

	return , nil
}

func (DateCodec) ( *Map,  uint32,  int16,  any) ScanPlan {
	switch  {
	case BinaryFormatCode:
		switch .(type) {
		case DateScanner:
			return scanPlanBinaryDateToDateScanner{}
		}
	case TextFormatCode:
		switch .(type) {
		case DateScanner:
			return scanPlanTextAnyToDateScanner{}
		}
	}

	return nil
}

type scanPlanBinaryDateToDateScanner struct{}

func (scanPlanBinaryDateToDateScanner) ( []byte,  any) error {
	 := ().(DateScanner)

	if  == nil {
		return .ScanDate(Date{})
	}

	if len() != 4 {
		return fmt.Errorf("invalid length for date: %v", len())
	}

	 := int32(binary.BigEndian.Uint32())

	switch  {
	case infinityDayOffset:
		return .ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	case negativeInfinityDayOffset:
		return .ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	default:
		 := time.Date(2000, 1, int(1+), 0, 0, 0, 0, time.UTC)
		return .ScanDate(Date{Time: , Valid: true})
	}
}

type scanPlanTextAnyToDateScanner struct{}

func (scanPlanTextAnyToDateScanner) ( []byte,  any) error {
	 := ().(DateScanner)

	if  == nil {
		return .ScanDate(Date{})
	}

	// Check infinity cases first
	if len() == 8 && string() == "infinity" {
		return .ScanDate(Date{InfinityModifier: Infinity, Valid: true})
	}
	if len() == 9 && string() == "-infinity" {
		return .ScanDate(Date{InfinityModifier: -Infinity, Valid: true})
	}

	// Format: YYYY-MM-DD or YYYY...-MM-DD BC
	// Minimum: 10 chars (2000-01-01), with BC: 13 chars
	if len() < 10 {
		return fmt.Errorf("invalid date format")
	}

	// Check for BC suffix
	 := false
	 := 
	if len() >= 13 && string([len()-3:]) == " BC" {
		 = true
		 = [:len()-3]
	}

	// Find year-month separator (first dash after at least 4 digits)
	 := -1
	for  := 4;  < len(); ++ {
		if [] == '-' {
			 = 
			break
		}
		if [] < '0' || [] > '9' {
			return fmt.Errorf("invalid date format")
		}
	}
	if  == -1 || +6 > len() {
		return fmt.Errorf("invalid date format")
	}

	// Validate: -MM-DD structure after year
	if [+3] != '-' {
		return fmt.Errorf("invalid date format")
	}

	// Parse year
	,  := parseDigits([:])
	if  != nil {
		return fmt.Errorf("invalid date format")
	}

	// Parse month (2 digits)
	,  := parse2Digits([+1 : +3])
	if  != nil {
		return fmt.Errorf("invalid date format")
	}

	// Parse day (2 digits)
	,  := parse2Digits([+4 : +6])
	if  != nil {
		return fmt.Errorf("invalid date format")
	}

	// Ensure nothing extra after day
	if +6 != len() {
		return fmt.Errorf("invalid date format")
	}

	if  {
		 = - + 1
	}

	 := time.Date(int(), time.Month(), int(), 0, 0, 0, 0, time.UTC)
	return .ScanDate(Date{Time: , Valid: true})
}

// parse2Digits parses exactly 2 ASCII digits.
func ( []byte) (int64, error) {
	if len() != 2 {
		return 0, fmt.Errorf("expected 2 digits")
	}
	,  := [0], [1]
	if  < '0' ||  > '9' ||  < '0' ||  > '9' {
		return 0, fmt.Errorf("expected digits")
	}
	return int64(-'0')*10 + int64(-'0'), nil
}

// parseDigits parses a sequence of ASCII digits.
func ( []byte) (int64, error) {
	if len() == 0 {
		return 0, fmt.Errorf("empty")
	}
	var  int64
	for ,  := range  {
		if  < '0' ||  > '9' {
			return 0, fmt.Errorf("non-digit")
		}
		 = *10 + int64(-'0')
	}
	return , nil
}

func ( DateCodec) ( *Map,  uint32,  int16,  []byte) (driver.Value, error) {
	if  == nil {
		return nil, nil
	}

	var  Date
	 := codecScan(, , , , , &)
	if  != nil {
		return nil, 
	}

	if .InfinityModifier != Finite {
		return .InfinityModifier.String(), nil
	}

	return .Time, nil
}

func ( DateCodec) ( *Map,  uint32,  int16,  []byte) (any, error) {
	if  == nil {
		return nil, nil
	}

	var  Date
	 := codecScan(, , , , , &)
	if  != nil {
		return nil, 
	}

	if .InfinityModifier != Finite {
		return .InfinityModifier, nil
	}

	return .Time, nil
}