using System;
using System.Collections.Generic;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;

namespace ClaudeDo.Ui.ViewModels;

/// <summary>Backs the footer/Mission-Control-header usage pill. One instance is shared by both hosts.</summary>
public sealed partial class UsagePillViewModel : ViewModelBase
{
    private readonly IWorkerClient _worker;

    [ObservableProperty]
    private UsageSnapshotDto? _snapshot;

    /// <summary>Host wires this to open the usage-monitor modal (not built yet — the command is a no-op until then).</summary>
    public event Action? OpenMonitorRequested;

    public UsagePillViewModel(IWorkerClient worker)
    {
        _worker = worker;
        _worker.UsageUpdatedEvent += OnUsageUpdated;
        _worker.ConnectionRestoredEvent += OnConnectionRestored;
        _ = LoadAsync();
    }

    private async System.Threading.Tasks.Task LoadAsync()
    {
        var snapshot = await _worker.GetUsageSnapshotAsync();
        if (snapshot is not null)
            Snapshot = snapshot;
    }

    private void OnUsageUpdated(UsageSnapshotDto snapshot) => Snapshot = snapshot;

    private void OnConnectionRestored() => _ = LoadAsync();

    partial void OnSnapshotChanged(UsageSnapshotDto? value)
    {
        OnPropertyChanged(nameof(Text));
        OnPropertyChanged(nameof(IsWarn));
        OnPropertyChanged(nameof(IsBlocked));
        OnPropertyChanged(nameof(IsStale));
        OnPropertyChanged(nameof(IsThrottled));
        OnPropertyChanged(nameof(Tooltip));
        OnPropertyChanged(nameof(ShowNormalDot));
        OnPropertyChanged(nameof(ShowWarnDot));
        OnPropertyChanged(nameof(ShowStaleDot));
    }

    public string Text => BuildText(Snapshot);

    public bool IsBlocked => Snapshot?.IsGateBlocked == true;

    public bool IsStale => Snapshot?.IsStale == true;

    public bool IsThrottled => Snapshot is { } s && !IsBlocked && s.EffectiveSlots < s.ConfiguredSlots;

    public bool IsWarn => Snapshot is { } s &&
        ((s.FiveHourPercent is { } five && five >= s.FiveHourThresholdPct - 10) ||
         (s.SevenDayPercent is { } seven && seven >= s.SevenDayThresholdPct - 10));

    // Visual dot priority: blocked > stale > warn > normal — mirrors the connection pill's
    // mutually-exclusive dot Ellipses, since Blocked/Stale/Warn aren't mutually exclusive states.
    public bool ShowNormalDot => !IsBlocked && !IsStale && !IsWarn;
    public bool ShowWarnDot => IsWarn && !IsBlocked && !IsStale;
    public bool ShowStaleDot => IsStale && !IsBlocked;

    public string Tooltip => BuildTooltip(Snapshot);

    [RelayCommand]
    private void OpenMonitor() => OpenMonitorRequested?.Invoke();

    private static string BuildText(UsageSnapshotDto? s)
    {
        if (s is null) return Loc.T("usage.pill.empty");

        var five = s.FiveHourPercent;
        var seven = s.SevenDayPercent;
        if (five is not null && seven is not null)
            return Loc.T("usage.pill.bothFormat", FormatPct(five.Value), FormatPct(seven.Value));
        if (five is not null)
            return Loc.T("usage.pill.fiveHourFormat", FormatPct(five.Value));
        if (seven is not null)
            return Loc.T("usage.pill.sevenDayFormat", FormatPct(seven.Value));
        return Loc.T("usage.pill.empty");
    }

    private static string FormatPct(double v) => Math.Round(v).ToString("0");

    private static string BuildTooltip(UsageSnapshotDto? s)
    {
        if (s is null) return Loc.T("usage.pill.empty");

        var lines = new List<string>();
        if (s.FiveHourResetsAt is { } fiveReset)
            lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.fiveHourLabel"), UsageTimeFormat.FormatRemaining(fiveReset)));
        if (s.SevenDayResetsAt is { } sevenReset)
            lines.Add(Loc.T("usage.pill.resetIn", Loc.T("usage.pill.sevenDayLabel"), UsageTimeFormat.FormatRemaining(sevenReset)));
        if (s.IsGateBlocked && !string.IsNullOrEmpty(s.GateReason))
            lines.Add(Loc.T("usage.pill.blockedReason", s.GateReason));
        else if (s.EffectiveSlots < s.ConfiguredSlots)
            lines.Add(Loc.T("usage.pill.throttled", s.EffectiveSlots, s.ConfiguredSlots, BucketLabel(s.ThrottleBucket)));
        if (s.IsStale)
        {
            var stamp = s.FetchedAtUtc?.ToLocalTime().ToString("HH:mm") ?? "?";
            lines.Add(Loc.T("usage.pill.stale", stamp));
            if (!string.IsNullOrEmpty(s.LastError))
                lines.Add(Loc.T("usage.pill.lastError", s.LastError));
        }

        return lines.Count > 0 ? string.Join(Environment.NewLine, lines) : Loc.T("usage.pill.empty");
    }

    private static string BucketLabel(string? bucket) => bucket switch
    {
        "five_hour" => Loc.T("usage.pill.fiveHourLabel"),
        "seven_day" => Loc.T("usage.pill.sevenDayLabel"),
        _ => "",
    };
}
