package sqlite3

import (
	
	
	
	

	
	
)

// TimeFormat specifies how to encode/decode time values.
//
// See the documentation for the [TimeFormatDefault] constant
// for formats recognized by SQLite.
//
// https://sqlite.org/lang_datefunc.html
type TimeFormat string

// TimeFormats recognized by SQLite to encode/decode time values.
//
// https://sqlite.org/lang_datefunc.html#time_values
const (
	TimeFormatDefault TimeFormat = "" // time.RFC3339Nano

	// Text formats
	TimeFormat1  TimeFormat = "2006-01-02"
	TimeFormat2  TimeFormat = "2006-01-02 15:04"
	TimeFormat3  TimeFormat = "2006-01-02 15:04:05"
	TimeFormat4  TimeFormat = "2006-01-02 15:04:05.000"
	TimeFormat5  TimeFormat = "2006-01-02T15:04"
	TimeFormat6  TimeFormat = "2006-01-02T15:04:05"
	TimeFormat7  TimeFormat = "2006-01-02T15:04:05.000"
	TimeFormat8  TimeFormat = "15:04"
	TimeFormat9  TimeFormat = "15:04:05"
	TimeFormat10 TimeFormat = "15:04:05.000"

	TimeFormat2TZ  = TimeFormat2 + "Z07:00"
	TimeFormat3TZ  = TimeFormat3 + "Z07:00"
	TimeFormat4TZ  = TimeFormat4 + "Z07:00"
	TimeFormat5TZ  = TimeFormat5 + "Z07:00"
	TimeFormat6TZ  = TimeFormat6 + "Z07:00"
	TimeFormat7TZ  = TimeFormat7 + "Z07:00"
	TimeFormat8TZ  = TimeFormat8 + "Z07:00"
	TimeFormat9TZ  = TimeFormat9 + "Z07:00"
	TimeFormat10TZ = TimeFormat10 + "Z07:00"

	// Numeric formats
	TimeFormatJulianDay TimeFormat = "julianday"
	TimeFormatUnix      TimeFormat = "unixepoch"
	TimeFormatUnixFrac  TimeFormat = "unixepoch_frac"
	TimeFormatUnixMilli TimeFormat = "unixepoch_milli" // not an SQLite format
	TimeFormatUnixMicro TimeFormat = "unixepoch_micro" // not an SQLite format
	TimeFormatUnixNano  TimeFormat = "unixepoch_nano"  // not an SQLite format

	// Auto
	TimeFormatAuto TimeFormat = "auto"
)

// Encode encodes a time value using this format.
//
// [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano],
// with nanosecond accuracy, and preserving any timezone offset.
//
// This is the format used by the [database/sql] driver:
// [database/sql.Row.Scan] will decode as [time.Time]
// values encoded with [time.RFC3339Nano].
//
// Time values encoded with [time.RFC3339Nano] cannot be sorted as strings
// to produce a time-ordered sequence.
//
// Assuming that the time zones of the time values are the same (e.g., all in UTC),
// and expressed using the same string (e.g., all "Z" or all "+00:00"),
// use the TIME [collating sequence] to produce a time-ordered sequence.
//
// Otherwise, use [TimeFormat7] for time-ordered encoding.
//
// Formats [TimeFormat1] through [TimeFormat10]
// convert time values to UTC before encoding.
//
// Returns a string for the text formats,
// a float64 for [TimeFormatJulianDay] and [TimeFormatUnixFrac],
// or an int64 for the other numeric formats.
//
// https://sqlite.org/lang_datefunc.html
//
// [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences
func ( TimeFormat) ( time.Time) any {
	switch  {
	// Numeric formats
	case TimeFormatJulianDay:
		return julianday.Float()
	case TimeFormatUnix:
		return .Unix()
	case TimeFormatUnixFrac:
		return math.FMA(1e-9, float64(.Nanosecond()), float64(.Unix()))
	case TimeFormatUnixMilli:
		return .UnixMilli()
	case TimeFormatUnixMicro:
		return .UnixMicro()
	case TimeFormatUnixNano:
		return .UnixNano()
	// Special formats.
	case TimeFormatDefault, TimeFormatAuto:
		 = time.RFC3339Nano
	// SQLite assumes UTC if unspecified.
	case
		TimeFormat1, TimeFormat2,
		TimeFormat3, TimeFormat4,
		TimeFormat5, TimeFormat6,
		TimeFormat7, TimeFormat8,
		TimeFormat9, TimeFormat10:
		 = .UTC()
	}
	return .Format(string())
}

// Decode decodes a time value using this format.
//
// The time value can be a string, an int64, or a float64.
//
// Formats [TimeFormat8] through [TimeFormat10]
// (and [TimeFormat8TZ] through [TimeFormat10TZ])
// assume a date of 2000-01-01.
//
// The timezone indicator and fractional seconds are always optional
// for formats [TimeFormat2] through [TimeFormat10]
// (and [TimeFormat2TZ] through [TimeFormat10TZ]).
//
// [TimeFormatAuto] implements (and extends) the SQLite auto modifier.
// Julian day numbers are safe to use for historical dates,
// from 4712BC through 9999AD.
// Unix timestamps (expressed in seconds, milliseconds, microseconds, or nanoseconds)
// are safe to use for current events, from at least 1980 through at least 2260.
// Unix timestamps before 1980 and after 9999 may be misinterpreted as julian day numbers,
// or have the wrong time unit.
//
// https://sqlite.org/lang_datefunc.html
func ( TimeFormat) ( any) (time.Time, error) {
	if ,  := .(time.Time);  {
		return , nil
	}
	switch  {
	// Numeric formats.
	case TimeFormatJulianDay:
		switch v := .(type) {
		case string:
			return julianday.Parse()
		case float64:
			return julianday.FloatTime(), nil
		case int64:
			return julianday.Time(, 0), nil
		default:
			return time.Time{}, util.TimeErr
		}

	case TimeFormatUnix, TimeFormatUnixFrac:
		if ,  := .(string);  {
			,  := strconv.ParseFloat(, 64)
			if  != nil {
				return time.Time{}, 
			}
			 = 
		}
		switch v := .(type) {
		case float64:
			,  := math.Modf()
			 := math.Floor( * 1e9)
			return time.Unix(int64(), int64()).UTC(), nil
		case int64:
			return time.Unix(, 0).UTC(), nil
		default:
			return time.Time{}, util.TimeErr
		}

	case TimeFormatUnixMilli:
		if ,  := .(string);  {
			,  := strconv.ParseInt(, 10, 64)
			if  != nil {
				return time.Time{}, 
			}
			 = 
		}
		switch v := .(type) {
		case float64:
			return time.UnixMilli(int64(math.Floor())).UTC(), nil
		case int64:
			return time.UnixMilli().UTC(), nil
		default:
			return time.Time{}, util.TimeErr
		}

	case TimeFormatUnixMicro:
		if ,  := .(string);  {
			,  := strconv.ParseInt(, 10, 64)
			if  != nil {
				return time.Time{}, 
			}
			 = 
		}
		switch v := .(type) {
		case float64:
			return time.UnixMicro(int64(math.Floor())).UTC(), nil
		case int64:
			return time.UnixMicro().UTC(), nil
		default:
			return time.Time{}, util.TimeErr
		}

	case TimeFormatUnixNano:
		if ,  := .(string);  {
			,  := strconv.ParseInt(, 10, 64)
			if  != nil {
				return time.Time{}, util.TimeErr
			}
			 = 
		}
		switch v := .(type) {
		case float64:
			return time.Unix(0, int64(math.Floor())).UTC(), nil
		case int64:
			return time.Unix(0, ).UTC(), nil
		default:
			return time.Time{}, util.TimeErr
		}

	// Special formats.
	case TimeFormatAuto:
		switch s := .(type) {
		case string:
			,  := strconv.ParseInt(, 10, 64)
			if  == nil {
				 = 
				break
			}
			,  := strconv.ParseFloat(, 64)
			if  == nil {
				 = 
				break
			}

			 := []TimeFormat{
				TimeFormat9, TimeFormat8,
				TimeFormat6, TimeFormat5,
				TimeFormat3, TimeFormat2, TimeFormat1,
			}
			for ,  := range  {
				,  := .()
				if  == nil {
					return , nil
				}
			}
		}
		switch v := .(type) {
		case float64:
			if 0 <=  &&  < 5373484.5 {
				return TimeFormatJulianDay.()
			}
			if  < 253402300800 {
				return TimeFormatUnixFrac.()
			}
			if  < 253402300800_000 {
				return TimeFormatUnixMilli.()
			}
			if  < 253402300800_000000 {
				return TimeFormatUnixMicro.()
			}
			return TimeFormatUnixNano.()
		case int64:
			if 0 <=  &&  < 5373485 {
				return TimeFormatJulianDay.()
			}
			if  < 253402300800 {
				return TimeFormatUnixFrac.()
			}
			if  < 253402300800_000 {
				return TimeFormatUnixMilli.()
			}
			if  < 253402300800_000000 {
				return TimeFormatUnixMicro.()
			}
			return TimeFormatUnixNano.()
		default:
			return time.Time{}, util.TimeErr
		}

	case
		TimeFormat2, TimeFormat2TZ,
		TimeFormat3, TimeFormat3TZ,
		TimeFormat4, TimeFormat4TZ,
		TimeFormat5, TimeFormat5TZ,
		TimeFormat6, TimeFormat6TZ,
		TimeFormat7, TimeFormat7TZ:
		,  := .(string)
		if ! {
			return time.Time{}, util.TimeErr
		}
		return .parseRelaxed()

	case
		TimeFormat8, TimeFormat8TZ,
		TimeFormat9, TimeFormat9TZ,
		TimeFormat10, TimeFormat10TZ:
		,  := .(string)
		if ! {
			return time.Time{}, util.TimeErr
		}
		,  := .parseRelaxed()
		if  != nil {
			return time.Time{}, 
		}
		return .AddDate(2000, 0, 0), nil

	default:
		,  := .(string)
		if ! {
			return time.Time{}, util.TimeErr
		}
		if  == "" {
			 = time.RFC3339Nano
		}
		return time.Parse(string(), )
	}
}

func ( TimeFormat) ( string) (time.Time, error) {
	 := string()
	 = strings.TrimSuffix(, "Z07:00")
	 = strings.TrimSuffix(, ".000")
	,  := time.Parse(+"Z07:00", )
	if  != nil {
		return time.Parse(, )
	}
	return , nil
}

// Scanner returns a [database/sql.Scanner] that can be used as an argument to
// [database/sql.Row.Scan] and similar methods to
// decode a time value into dest using this format.
func ( TimeFormat) ( *time.Time) interface{ (any) error } {
	return timeScanner{, }
}

type timeScanner struct {
	*time.Time
	TimeFormat
}

func ( timeScanner) ( any) error {
	var  bool
	var  error
	if *.Time,  = .(time.Time); ! {
		*.Time,  = .Decode()
	}
	return 
}