package cmd

import (
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/clinic-cli/internal/timeexpr"
)

func parseFlexibleTimeToUnixNano(raw string, loc *time.Location) (int64, error) {
	return parseFlexibleTimeToUnixNanoWithNow(raw, loc, time.Now())
}

func parseFlexibleTimeToUnixNanoWithNow(raw string, loc *time.Location, now time.Time) (int64, error) {
	value := strings.TrimSpace(raw)
	if value == "" {
		return 0, fmt.Errorf("time value must not be empty")
	}

	if loc == nil {
		loc = time.Local
	}

	if nowValue, matched, err := timeexpr.ParseNowExpressionToUnixNano(value, now); matched {
		if err != nil {
			return 0, err
		}
		return nowValue, nil
	}

	if numericValue, matched, err := parseNumericTimeToUnixNano(value); matched {
		if err != nil {
			return 0, err
		}
		return numericValue, nil
	}

	for _, layout := range timestampLayoutsWithZone {
		parsed, err := time.Parse(layout, value)
		if err == nil {
			return parsed.UnixNano(), nil
		}
	}

	for _, layout := range timestampLayoutsWithoutZone {
		parsed, err := time.ParseInLocation(layout, value, loc)
		if err == nil {
			return parsed.UnixNano(), nil
		}
	}

	return 0, fmt.Errorf(
		"unsupported time format %q (expected \"now\", \"now-<duration>\" with d as 24h, datetime like \"2006-01-02 15:04:05\", or epoch in s/ms/ns)",
		raw,
	)
}

var timestampLayoutsWithZone = []string{
	time.RFC3339,
	time.RFC3339Nano,
	"2006-01-02 15:04:05 -0700",
	"2006-01-02 15:04:05 -07:00",
	"2006-01-02 15:04:05 MST",
	"2006-01-02T15:04:05 -0700",
	"2006-01-02T15:04:05 -07:00",
	"2006-01-02T15:04:05 MST",
}

var timestampLayoutsWithoutZone = []string{
	"2006-01-02 15:04:05",
	"2006-01-02T15:04:05",
}

func parseNumericTimeToUnixNano(value string) (int64, bool, error) {
	for _, unit := range []string{"ns", "ms", "s"} {
		if !strings.HasSuffix(value, unit) {
			continue
		}

		numberText := strings.TrimSpace(strings.TrimSuffix(value, unit))
		if numberText == "" {
			return 0, true, fmt.Errorf("invalid epoch value %q", value)
		}

		number, err := strconv.ParseInt(numberText, 10, 64)
		if err != nil {
			return 0, true, fmt.Errorf("invalid epoch value %q", value)
		}

		switch unit {
		case "ns":
			return number, true, nil
		case "ms":
			converted, err := safeMultiplyInt64(number, int64(time.Millisecond))
			if err != nil {
				return 0, true, err
			}
			return converted, true, nil
		case "s":
			converted, err := safeMultiplyInt64(number, int64(time.Second))
			if err != nil {
				return 0, true, err
			}
			return converted, true, nil
		}
	}

	number, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return 0, false, nil
	}

	digits := value
	if strings.HasPrefix(digits, "+") || strings.HasPrefix(digits, "-") {
		digits = digits[1:]
	}

	switch {
	case len(digits) >= 18:
		return number, true, nil
	case len(digits) >= 13:
		converted, err := safeMultiplyInt64(number, int64(time.Millisecond))
		if err != nil {
			return 0, true, err
		}
		return converted, true, nil
	default:
		converted, err := safeMultiplyInt64(number, int64(time.Second))
		if err != nil {
			return 0, true, err
		}
		return converted, true, nil
	}
}

func safeMultiplyInt64(value, multiplier int64) (int64, error) {
	if value == 0 || multiplier == 0 {
		return 0, nil
	}
	if value > math.MaxInt64/multiplier || value < math.MinInt64/multiplier {
		return 0, fmt.Errorf("epoch value overflows int64")
	}
	return value * multiplier, nil
}
