using System.Globalization;
using System.Text.Json;

namespace AxCopilot.Services.Agent;

/// <summary>날짜·시간 변환, 타임존, 기간 계산 유틸리티 도구.</summary>
public class DateTimeTool : IAgentTool
{
    public string Name => "datetime_tool";
    public string Description =>
        "Date/time utility tool. Actions: " +
        "'now' — get current date/time in various formats; " +
        "'parse' — parse a date string into standard format; " +
        "'diff' — calculate difference between two dates; " +
        "'add' — add/subtract days/hours/minutes to a date; " +
        "'epoch' — convert between Unix epoch and datetime; " +
        "'format' — format a date into specified pattern.";

    public ToolParameterSchema Parameters => new()
    {
        Properties = new()
        {
            ["action"] = new()
            {
                Type = "string",
                Description = "Action to perform",
                Enum = ["now", "parse", "diff", "add", "epoch", "format"],
            },
            ["date"] = new()
            {
                Type = "string",
                Description = "Date string (for parse/diff/add/format/epoch). For epoch: Unix timestamp in seconds.",
            },
            ["date2"] = new()
            {
                Type = "string",
                Description = "Second date string (for diff action)",
            },
            ["amount"] = new()
            {
                Type = "string",
                Description = "Amount to add (for add action). E.g. '7' for 7 days",
            },
            ["unit"] = new()
            {
                Type = "string",
                Description = "Unit for add action",
                Enum = ["days", "hours", "minutes", "seconds", "months", "years"],
            },
            ["pattern"] = new()
            {
                Type = "string",
                Description = "Format pattern (for format action). E.g. 'yyyy-MM-dd HH:mm:ss', 'ddd MMM d yyyy'",
            },
        },
        Required = ["action"],
    };

    public Task<ToolResult> ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default)
    {
        var action = args.GetProperty("action").GetString() ?? "";

        try
        {
            return Task.FromResult(action switch
            {
                "now"    => Now(),
                "parse"  => Parse(args),
                "diff"   => Diff(args),
                "add"    => Add(args),
                "epoch"  => Epoch(args),
                "format" => FormatDate(args),
                _        => ToolResult.Fail($"Unknown action: {action}"),
            });
        }
        catch (Exception ex)
        {
            return Task.FromResult(ToolResult.Fail($"DateTime 오류: {ex.Message}"));
        }
    }

    private static ToolResult Now()
    {
        var now = DateTime.Now;
        var utc = DateTime.UtcNow;
        var epoch = new DateTimeOffset(utc).ToUnixTimeSeconds();
        return ToolResult.Ok(
            $"Local:  {now:yyyy-MM-dd HH:mm:ss (ddd)} ({TimeZoneInfo.Local.DisplayName})\n" +
            $"UTC:    {utc:yyyy-MM-dd HH:mm:ss}\n" +
            $"ISO:    {now:O}\n" +
            $"Epoch:  {epoch}\n" +
            $"Week:   {CultureInfo.InvariantCulture.Calendar.GetWeekOfYear(now, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday)}");
    }

    private static ToolResult Parse(JsonElement args)
    {
        var dateStr = args.TryGetProperty("date", out var d) ? d.GetString() ?? "" : "";
        if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required");

        if (!DateTime.TryParse(dateStr, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt) &&
            !DateTime.TryParse(dateStr, CultureInfo.CurrentCulture, DateTimeStyles.None, out dt))
            return ToolResult.Fail($"Cannot parse date: '{dateStr}'");

        return ToolResult.Ok(
            $"Parsed:  {dt:yyyy-MM-dd HH:mm:ss}\n" +
            $"Day:     {dt:dddd}\n" +
            $"ISO:     {dt:O}\n" +
            $"Epoch:   {new DateTimeOffset(dt).ToUnixTimeSeconds()}");
    }

    private static ToolResult Diff(JsonElement args)
    {
        var d1 = args.TryGetProperty("date", out var v1) ? v1.GetString() ?? "" : "";
        var d2 = args.TryGetProperty("date2", out var v2) ? v2.GetString() ?? "" : "";
        if (string.IsNullOrEmpty(d1) || string.IsNullOrEmpty(d2))
            return ToolResult.Fail("'date' and 'date2' parameters are required");

        if (!DateTime.TryParse(d1, out var dt1)) return ToolResult.Fail($"Cannot parse date: '{d1}'");
        if (!DateTime.TryParse(d2, out var dt2)) return ToolResult.Fail($"Cannot parse date: '{d2}'");

        var diff = dt2 - dt1;
        return ToolResult.Ok(
            $"From: {dt1:yyyy-MM-dd HH:mm:ss}\n" +
            $"To:   {dt2:yyyy-MM-dd HH:mm:ss}\n\n" +
            $"Difference:\n" +
            $"  {Math.Abs(diff.TotalDays):F1} days\n" +
            $"  {Math.Abs(diff.TotalHours):F1} hours\n" +
            $"  {Math.Abs(diff.TotalMinutes):F0} minutes\n" +
            $"  {Math.Abs(diff.TotalSeconds):F0} seconds\n" +
            $"  ({(diff.TotalDays >= 0 ? "forward" : "backward")})");
    }

    private static ToolResult Add(JsonElement args)
    {
        var dateStr = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : "";
        var amountStr = args.TryGetProperty("amount", out var av) ? av.GetString() ?? "0" : "0";
        var unit = args.TryGetProperty("unit", out var uv) ? uv.GetString() ?? "days" : "days";

        if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required");
        if (!DateTime.TryParse(dateStr, out var dt)) return ToolResult.Fail($"Cannot parse date: '{dateStr}'");
        if (!double.TryParse(amountStr, out var amount)) return ToolResult.Fail($"Invalid amount: '{amountStr}'");

        var result = unit switch
        {
            "days"    => dt.AddDays(amount),
            "hours"   => dt.AddHours(amount),
            "minutes" => dt.AddMinutes(amount),
            "seconds" => dt.AddSeconds(amount),
            "months"  => dt.AddMonths((int)amount),
            "years"   => dt.AddYears((int)amount),
            _         => dt.AddDays(amount),
        };

        return ToolResult.Ok(
            $"Original: {dt:yyyy-MM-dd HH:mm:ss}\n" +
            $"Added:    {amount} {unit}\n" +
            $"Result:   {result:yyyy-MM-dd HH:mm:ss} ({result:dddd})");
    }

    private static ToolResult Epoch(JsonElement args)
    {
        var input = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : "";
        if (string.IsNullOrEmpty(input)) return ToolResult.Fail("'date' parameter is required");

        // 숫자면 epoch → datetime
        if (long.TryParse(input, out var epoch))
        {
            var dt = DateTimeOffset.FromUnixTimeSeconds(epoch);
            return ToolResult.Ok(
                $"Epoch:  {epoch}\n" +
                $"UTC:    {dt.UtcDateTime:yyyy-MM-dd HH:mm:ss}\n" +
                $"Local:  {dt.LocalDateTime:yyyy-MM-dd HH:mm:ss}");
        }

        // 문자열이면 datetime → epoch
        if (DateTime.TryParse(input, out var parsed))
        {
            var e = new DateTimeOffset(parsed).ToUnixTimeSeconds();
            return ToolResult.Ok(
                $"Date:   {parsed:yyyy-MM-dd HH:mm:ss}\n" +
                $"Epoch:  {e}");
        }

        return ToolResult.Fail($"Cannot parse: '{input}'");
    }

    private static ToolResult FormatDate(JsonElement args)
    {
        var dateStr = args.TryGetProperty("date", out var dv) ? dv.GetString() ?? "" : "";
        var pattern = args.TryGetProperty("pattern", out var pv) ? pv.GetString() ?? "yyyy-MM-dd" : "yyyy-MM-dd";

        if (string.IsNullOrEmpty(dateStr)) return ToolResult.Fail("'date' parameter is required");
        if (!DateTime.TryParse(dateStr, out var dt)) return ToolResult.Fail($"Cannot parse date: '{dateStr}'");

        return ToolResult.Ok(dt.ToString(pattern, CultureInfo.InvariantCulture));
    }
}
