#!/usr/bin/env python3
import datetime
import sys
import argparse
import re
import time

YELLOW = "\033[33m"
RESET = "\033[0m"


def is_timestamp(input_str):
    # Check if the input is a numeric timestamp (integer or float)
    return input_str.replace(".", "").isdigit()


def format_time(timestamp, verbose=False):
    # Convert to float to handle potential decimal points
    timestamp = float(timestamp)

    # Determine if timestamp is in milliseconds or seconds
    if timestamp >= 1000000000000:
        if verbose:
            print(f"Detected timestamp in milliseconds: {timestamp}")
        timestamp_ms = timestamp
        timestamp_s = round(timestamp / 1000.0)
    else:
        if verbose:
            print(f"Detected timestamp in seconds: {timestamp}")
        timestamp_s = timestamp
        timestamp_ms = timestamp * 1000.0

    # Convert to datetime objects
    utc_timezone = datetime.timezone.utc
    utc_time = datetime.datetime.fromtimestamp(timestamp_s, tz=utc_timezone)
    local_time = datetime.datetime.fromtimestamp(timestamp_s)

    # Format times
    time_format = "%Y-%m-%d %H:%M:%S"

    print(f"ts seconds: {YELLOW}{timestamp_s}{RESET}")
    print(f"ts ms:      {YELLOW}{int(timestamp_ms)}{RESET}")
    print(
        f"Local:      {YELLOW}{local_time.strftime(time_format)} {local_time.astimezone().tzname()}{RESET}"
    )
    print(f"UTC:        {YELLOW}{utc_time.strftime(time_format)} UTC{RESET}")


def parse_date_string(date_string, verbose=False, assume_utc=False):
    if verbose:
        print(f"Parsing date string: {date_string}")

    # Handle ISO format with nanoseconds (Python's %f only handles microseconds)
    # Match patterns like: 2026-04-09T15:02:20.62254323Z
    nano_match = re.match(
        r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.(\d+)Z", date_string
    )
    if nano_match:
        base_str, frac_str = nano_match.groups()
        # Truncate to microseconds (6 digits) for Python's datetime
        micro_str = frac_str[:6].ljust(6, '0')
        try:
            dt = datetime.datetime.strptime(
                f"{base_str}.{micro_str}Z", "%Y-%m-%dT%H:%M:%S.%fZ"
            )
            dt = dt.replace(tzinfo=datetime.timezone.utc)
            if verbose:
                print(f"Parsed ISO format with nanoseconds (truncated to microseconds): {dt}")
            return dt.timestamp()
        except ValueError:
            pass

    # Common format patterns
    # Format: (pattern, is_explicit_utc)
    # is_explicit_utc=True means the format itself indicates UTC (Z suffix, GMT, etc.)
    # is_explicit_utc=False means no timezone info, will use assume_utc flag
    format_patterns = [
        # ISO formats
        ("%Y-%m-%d", False),
        ("%Y-%m-%dT%H:%M", False),
        ("%Y-%m-%dT%H:%M:%S", False),
        ("%Y-%m-%dT%H:%M:%S.%fZ", True),  # UTC with microseconds
        ("%Y-%m-%dT%H:%M:%SZ", True),  # UTC
        # Common datetime formats (space-separated)
        ("%Y-%m-%d %H:%M:%S.%f", False),  # 2026-05-07 09:51:50.271
        ("%Y-%m-%d %H:%M:%S", False),  # 2026-05-07 09:51:50
        ("%Y-%m-%d %H:%M", False),  # 2026-05-07 09:51
        # Common formats
        ("%B %d %Y", False),  # March 21 2023
        ("%B %d %Y %H:%M:%S", False),  # March 21 2023 10:34:56
        ("%b %d %Y", False),  # Mar 21 2023
        ("%b %d %Y %H:%M:%S", False),  # Mar 21 2023 10:34:56
        # RFC formats
        ("%a, %d %b %Y %H:%M:%S GMT", True),  # Fri, 21 Mar 2025 13:57:36 GMT
    ]

    # Try ISO format with timezone offset (special handling required)
    iso_tz_match = re.match(
        r"(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})([+-]\d{2}:?\d{2})", date_string
    )
    if iso_tz_match:
        base_str, tz_str = iso_tz_match.groups()
        # Fix timezone format for parsing
        tz_str = tz_str.replace(":", "")
        if len(tz_str) == 3:  # +09 → +0900
            tz_str = f"{tz_str}00"
        try:
            dt = datetime.datetime.strptime(
                f"{base_str}{tz_str}", "%Y-%m-%dT%H:%M:%S%z"
            )
            if verbose:
                print(f"Parsed ISO timezone format: {dt}")
            return dt.timestamp()
        except ValueError:
            pass

    # Try each format pattern
    for pattern, is_explicit_utc in format_patterns:
        try:
            dt = datetime.datetime.strptime(date_string, pattern)
            
            if is_explicit_utc:
                # Format explicitly indicates UTC
                dt = dt.replace(tzinfo=datetime.timezone.utc)
            elif assume_utc:
                # User requested UTC interpretation
                dt = dt.replace(tzinfo=datetime.timezone.utc)
            else:
                # No timezone info, treat as local time and warn
                print(f"{YELLOW}Warning: No timezone specified, assuming local time. Use --utc to interpret as UTC.{RESET}")

            if verbose:
                tz_str = "UTC (explicit)" if is_explicit_utc else ("UTC (--utc flag)" if assume_utc else "local time")
                print(f"Matched pattern {pattern}: {dt} as {tz_str}")

            return dt.timestamp()
        except ValueError:
            continue

    # Try additional custom parsing for "Month Day Year" format
    try:
        # Try to match "March 21, 2023" or "March 21 2023"
        month_day_match = re.match(
            r"([A-Za-z]+)\s+(\d{1,2})(?:,|\s)+(\d{4})", date_string
        )
        if month_day_match:
            month, day, year = month_day_match.groups()
            month_num = {
                "january": 1,
                "february": 2,
                "march": 3,
                "april": 4,
                "may": 5,
                "june": 6,
                "july": 7,
                "august": 8,
                "september": 9,
                "october": 10,
                "november": 11,
                "december": 12,
            }
            month_lower = month.lower()
            if month_lower in month_num or month_lower[:3] in [
                k[:3] for k in month_num.keys()
            ]:
                if month_lower in month_num:
                    month_val = month_num[month_lower]
                else:
                    for k, v in month_num.items():
                        if k[:3] == month_lower[:3]:
                            month_val = v
                            break
                dt = datetime.datetime(int(year), month_val, int(day))
                if assume_utc:
                    dt = dt.replace(tzinfo=datetime.timezone.utc)
                else:
                    print(f"{YELLOW}Warning: No timezone specified, assuming local time. Use --utc to interpret as UTC.{RESET}")
                if verbose:
                    tz_str = "UTC (--utc flag)" if assume_utc else "local time"
                    print(f"Parsed with custom month-day-year logic: {dt} as {tz_str}")
                return dt.timestamp()
    except Exception:
        pass

    raise ValueError(f"Could not parse date string: {date_string}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Convert timestamps or date strings to various formats"
    )
    parser.add_argument("input", nargs="*", help="Unix timestamp or date string")
    parser.add_argument(
        "-v", "--verbose", action="store_true", help="Enable verbose output"
    )
    parser.add_argument(
        "-u", "--utc", action="store_true", help="Interpret date strings without timezone info as UTC instead of local time"
    )

    args = parser.parse_args()

    verbose = args.verbose

    if not args.input:
        input_string = str(int(time.time()))
        if verbose:
            print(f"No input provided. Using current timestamp: {input_string}")
    else:
        input_string = " ".join(args.input)

    if verbose:
        print(f"input: {input_string}")

    try:
        if is_timestamp(input_string):
            format_time(input_string, verbose)
        else:
            # Handle date string
            timestamp = parse_date_string(input_string, verbose, args.utc)
            format_time(timestamp, verbose)
    except ValueError as e:
        print(f"Error: {e}")
        print("Please provide a valid Unix timestamp or a recognizable date string.")
        sys.exit(1)
