using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Modals;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;

namespace ClaudeDo.Ui.ViewModels.Islands;

public enum ListKind { Smart, Virtual, User }

public sealed partial class ListsIslandViewModel : ViewModelBase
{
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly IServiceProvider? _services;
    private readonly WorkerClient? _worker;

    public event EventHandler? SelectionChanged;
    public event EventHandler? FocusSearchRequested;
    public void RequestFocusSearch() => FocusSearchRequested?.Invoke(this, EventArgs.Empty);

    public Func<SettingsModalViewModel, Task>? ShowSettingsModal { get; set; }
    public Func<ListSettingsModalViewModel, System.Threading.Tasks.Task>? ShowListSettingsModal { get; set; }

    [RelayCommand]
    private async Task OpenSettings()
    {
        if (ShowSettingsModal is null || _services is null) return;
        var settingsVm = _services.GetRequiredService<SettingsModalViewModel>();
        await settingsVm.LoadAsync();
        await ShowSettingsModal(settingsVm);
    }

    [RelayCommand]
    private async System.Threading.Tasks.Task OpenListSettingsAsync(ListNavItemViewModel? row)
    {
        if (row is null || ShowListSettingsModal is null || _services is null) return;
        var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
        await vm.LoadAsync(row.Id, row.Name, row.WorkingDir, row.DefaultCommitType);
        await ShowListSettingsModal(vm);
        await RefreshRowAsync(row.Id);
    }

    public ObservableCollection<ListNavItemViewModel> Items { get; } = new();
    public ObservableCollection<ListNavItemViewModel> SmartLists { get; } = new();
    public ObservableCollection<ListNavItemViewModel> UserLists { get; } = new();

    [ObservableProperty] private string _searchText = "";
    [ObservableProperty] private ListNavItemViewModel? _selectedList;

    public string UserName { get; } = Environment.UserName;
    public string MachineName { get; } = Environment.MachineName;
    public string UserInitials { get; }

    public ListsIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IServiceProvider? services = null, WorkerClient? worker = null)
    {
        _dbFactory = dbFactory;
        _services = services;
        _worker = worker;
        var parts = Environment.UserName.Split('.', '_', '-', ' ');
        UserInitials = parts.Length >= 2
            ? $"{parts[0][0]}{parts[1][0]}".ToUpperInvariant()
            : Environment.UserName.Length >= 2
                ? Environment.UserName[..2].ToUpperInvariant()
                : Environment.UserName.ToUpperInvariant();

        if (_worker is not null)
        {
            _worker.ListUpdatedEvent  += id => _ = RefreshRowAsync(id);
            _worker.TaskStartedEvent  += (_slot, _id, _at)             => _ = RefreshCountsAsync();
            _worker.TaskFinishedEvent += (_slot, _id, _status, _at)    => _ = RefreshCountsAsync();
            _worker.TaskUpdatedEvent  += _id                           => _ = RefreshCountsAsync();
        }
    }

    public async Task LoadAsync(CancellationToken ct = default)
    {
        Items.Clear();
        SmartLists.Clear();
        UserLists.Clear();

        var smart = new[]
        {
            new ListNavItemViewModel { Id = "smart:my-day",    Name = "My Day",    Kind = ListKind.Smart,   IconKey = "Sun" },
            new ListNavItemViewModel { Id = "smart:important", Name = "Important", Kind = ListKind.Smart,   IconKey = "Star" },
            new ListNavItemViewModel { Id = "smart:planned",   Name = "Planned",   Kind = ListKind.Smart,   IconKey = "Calendar" },
            new ListNavItemViewModel { Id = "virtual:queued",  Name = "Queue",     Kind = ListKind.Virtual, IconKey = "Inbox" },
            new ListNavItemViewModel { Id = "virtual:running", Name = "Running",   Kind = ListKind.Virtual, IconKey = "Activity" },
            new ListNavItemViewModel { Id = "virtual:review",  Name = "Review",    Kind = ListKind.Virtual, IconKey = "Eye" },
        };
        foreach (var s in smart) { Items.Add(s); SmartLists.Add(s); }

        await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
        var lists = new ListRepository(ctx);
        var seedNames = new HashSet<string>(new[] { "My Day", "Important", "Planned" });
        var dotColors = new[] { "Moss", "Peat", "Sage" };
        int idx = 0;
        foreach (var l in await lists.GetAllAsync(ct))
        {
            if (seedNames.Contains(l.Name)) continue;
            var item = new ListNavItemViewModel
            {
                Id = $"user:{l.Id}",
                Name = l.Name,
                Kind = ListKind.User,
                IconKey = "Folder",
                DotColorKey = dotColors[idx % dotColors.Length],
                WorkingDir = l.WorkingDir,
                DefaultCommitType = l.DefaultCommitType,
            };
            Items.Add(item);
            UserLists.Add(item);
            idx++;
        }

        await RefreshCountsAsync(ct);
        SelectedList = Items.FirstOrDefault();
    }

    public async Task RefreshCountsAsync(CancellationToken ct = default)
    {
        try
        {
            await using var ctx = await _dbFactory.CreateDbContextAsync(ct);

            // Snapshot the open (non-Done) tasks once; small enough collection for client-side grouping.
            var open = await ctx.Tasks.AsNoTracking()
                .Where(t => t.Status != TaskStatus.Done)
                .Select(t => new { t.ListId, t.Status, t.IsMyDay, t.IsStarred, Scheduled = t.ScheduledFor })
                .ToListAsync(ct);

            var running = open.Count(t => t.Status == TaskStatus.Running);
            var queued = open.Count(t => t.Status == TaskStatus.Queued);
            var review = await ctx.Tasks.AsNoTracking()
                .Where(t => t.Status == TaskStatus.Done && t.Worktree != null && t.Worktree.State == WorktreeState.Active)
                .CountAsync(ct);

            foreach (var item in SmartLists)
            {
                item.Count = item.Id switch
                {
                    "smart:my-day"    => open.Count(t => t.IsMyDay),
                    "smart:important" => open.Count(t => t.IsStarred),
                    "smart:planned"   => open.Count(t => t.Scheduled != null),
                    "virtual:queued"  => queued,
                    "virtual:running" => running,
                    "virtual:review"  => review,
                    _ => 0,
                };
            }

            foreach (var item in UserLists)
            {
                var listId = item.Id.StartsWith("user:", StringComparison.Ordinal)
                    ? item.Id["user:".Length..]
                    : item.Id;
                item.Count = open.Count(t => t.ListId == listId);
            }
        }
        catch (OperationCanceledException) { throw; }
        catch { /* best-effort refresh */ }
    }

    [RelayCommand]
    private void Select(ListNavItemViewModel item) => SelectedList = item;

    partial void OnSelectedListChanged(ListNavItemViewModel? value)
    {
        foreach (var i in Items) i.IsActive = ReferenceEquals(i, value);
        SelectionChanged?.Invoke(this, EventArgs.Empty);
    }

    private async System.Threading.Tasks.Task RefreshRowAsync(string rowId)
    {
        try
        {
            var rawId = rowId.StartsWith("user:") ? rowId["user:".Length..] : rowId;
            var row = UserLists.FirstOrDefault(r => r.Id == rowId);
            if (row is null) return;

            await using var ctx = await _dbFactory.CreateDbContextAsync();
            var lists = new ListRepository(ctx);
            var entity = await lists.GetByIdAsync(rawId);
            if (entity is null) return;

            row.WorkingDir = entity.WorkingDir;
            row.DefaultCommitType = entity.DefaultCommitType;
        }
        catch { /* best-effort refresh */ }
    }
}
