using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data;
using ClaudeDo.Data.Filtering;
using ClaudeDo.Data.Models;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Localization;
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, IDisposable
{
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly IServiceProvider? _services;
    private readonly IWorkerClient? _worker;
    private static readonly TaskListFilterRegistry _filters = new();

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

    public IDialogService? Dialogs { get; set; }

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

    [RelayCommand]
    private async System.Threading.Tasks.Task OpenListSettingsAsync(ListNavItemViewModel? row)
    {
        if (row is null || Dialogs is null || _services is null) return;
        var rawId = row.Id.StartsWith("user:", StringComparison.Ordinal) ? row.Id["user:".Length..] : row.Id;
        var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
        await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType);
        await Dialogs.ShowListSettingsAsync(vm);
        if (vm.Deleted) await LoadAsync();
        else await RefreshRowAsync(row.Id);
    }

    [RelayCommand]
    private async System.Threading.Tasks.Task OpenRepoImportAsync()
    {
        if (Dialogs is null || _services is null) return;
        var vm = _services.GetRequiredService<RepoImportModalViewModel>();
        await vm.LoadAsync();
        await Dialogs.ShowRepoImportAsync(vm);
        await LoadAsync();
    }

    private bool _worktreesOverviewOpen;

    [RelayCommand]
    private async Task OpenWorktreesOverviewAsync(ListNavItemViewModel? row)
    {
        if (row is null || Dialogs is null || _services is null) return;
        if (row.Kind != ListKind.User) return;
        if (_worktreesOverviewOpen) return;
        _worktreesOverviewOpen = true;
        try
        {
            var rawId = row.Id.StartsWith("user:", StringComparison.Ordinal) ? row.Id["user:".Length..] : row.Id;
            var vm = _services.GetRequiredService<WorktreesOverviewModalViewModel>();
            vm.Configure(rawId, row.Name);
            await vm.LoadAsync();
            await Dialogs.ShowWorktreesOverviewAsync(vm);
        }
        finally { _worktreesOverviewOpen = false; }
    }

    [RelayCommand]
    private void OpenInExplorer(ListNavItemViewModel? row)
    {
        var dir = row?.WorkingDir;
        if (string.IsNullOrWhiteSpace(dir) || !System.IO.Directory.Exists(dir)) return;
        try
        {
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
            {
                FileName = dir,
                UseShellExecute = true,
            });
        }
        catch { /* best-effort */ }
    }

    [RelayCommand]
    private void OpenInTerminal(ListNavItemViewModel? row)
    {
        var dir = row?.WorkingDir;
        if (string.IsNullOrWhiteSpace(dir) || !System.IO.Directory.Exists(dir)) return;
        try
        {
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
            {
                FileName = "wt.exe",
                Arguments = $"-d \"{dir}\"",
                UseShellExecute = true,
            });
        }
        catch
        {
            // Windows Terminal not installed — fall back to a plain console at the directory.
            try
            {
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
                {
                    FileName = "cmd.exe",
                    WorkingDirectory = dir,
                    UseShellExecute = true,
                });
            }
            catch { /* best-effort */ }
        }
    }

    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 MachineNameLocal => Loc.T("vm.lists.localSuffix", MachineName);
    public string UserInitials { get; }

    private readonly EventHandler _langChangedHandler;

    public ListsIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IServiceProvider? services = null, IWorkerClient? 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();
            _worker.WorktreeUpdatedEvent += _id                        => _ = RefreshCountsAsync();
            _worker.ConnectionRestoredEvent += ()                      => _ = RefreshCountsAsync();
        }

        _langChangedHandler = (_, _) => RefreshLocalizedLabels();
        Loc.LanguageChanged += _langChangedHandler;
    }

    public void Dispose()
    {
        Loc.LanguageChanged -= _langChangedHandler;
    }

    private static string? SmartListNameKey(string id) => id switch
    {
        "smart:my-day"    => "vm.lists.smartMyDay",
        "smart:important" => "vm.lists.smartImportant",
        "smart:planned"   => "vm.lists.smartPlanned",
        "virtual:queued"  => "vm.lists.virtualQueue",
        "virtual:running" => "vm.lists.virtualRunning",
        "virtual:review"  => "vm.lists.virtualReview",
        _                 => null,
    };

    private void RefreshLocalizedLabels()
    {
        foreach (var item in SmartLists)
            if (SmartListNameKey(item.Id) is { } key) item.Name = Loc.T(key);
        OnPropertyChanged(nameof(MachineNameLocal));
    }

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

        var smart = new[]
        {
            new ListNavItemViewModel { Id = "smart:my-day",    Name = Loc.T("vm.lists.smartMyDay"),      Kind = ListKind.Smart,   IconKey = "Sun" },
            new ListNavItemViewModel { Id = "smart:important", Name = Loc.T("vm.lists.smartImportant"),  Kind = ListKind.Smart,   IconKey = "Star" },
            new ListNavItemViewModel { Id = "smart:planned",   Name = Loc.T("vm.lists.smartPlanned"),    Kind = ListKind.Smart,   IconKey = "Calendar" },
            new ListNavItemViewModel { Id = "virtual:queued",  Name = Loc.T("vm.lists.virtualQueue"),    Kind = ListKind.Virtual, IconKey = "Inbox" },
            new ListNavItemViewModel { Id = "virtual:running", Name = Loc.T("vm.lists.virtualRunning"),  Kind = ListKind.Virtual, IconKey = "Activity" },
            new ListNavItemViewModel { Id = "virtual:review",  Name = Loc.T("vm.lists.virtualReview"),   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);

            // Single snapshot; counters and the list loader share the same filter strategies.
            var all = await ctx.Tasks.AsNoTracking()
                .Include(t => t.Worktree)
                .ToListAsync(ct);

            foreach (var item in SmartLists)
            {
                var filter = _filters.Resolve(item.Id);
                item.Count = filter is null ? 0 : all.Count(filter.ShouldCount);
            }

            foreach (var item in UserLists)
            {
                var filter = _filters.Resolve(item.Id);
                item.Count = filter is null ? 0 : all.Count(filter.ShouldCount);
            }
        }
        catch (OperationCanceledException) { throw; }
        catch { /* best-effort refresh */ }
    }

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

    [RelayCommand]
    private async Task CreateListAsync()
    {
        var entity = new ListEntity
        {
            Id = Guid.NewGuid().ToString("N"),
            Name = Loc.T("vm.lists.newList"),
            DefaultCommitType = CommitTypeRegistry.DefaultType,
            CreatedAt = DateTime.UtcNow,
        };

        await using (var ctx = await _dbFactory.CreateDbContextAsync())
        {
            var lists = new ListRepository(ctx);
            await lists.AddAsync(entity);
        }

        var item = new ListNavItemViewModel
        {
            Id = $"user:{entity.Id}",
            Name = entity.Name,
            Kind = ListKind.User,
            IconKey = "Folder",
            DotColorKey = "Moss",
            WorkingDir = entity.WorkingDir,
            DefaultCommitType = entity.DefaultCommitType,
        };
        Items.Add(item);
        UserLists.Add(item);
        SelectedList = item;

        if (Dialogs is not null && _services is not null)
        {
            var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
            await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType);
            await Dialogs.ShowListSettingsAsync(vm);
            if (vm.Deleted) await LoadAsync();
            else await RefreshRowAsync(item.Id);
        }
    }

    public void ClearDropHints()
    {
        foreach (var r in UserLists)
        {
            r.DropHintAbove = false;
            r.DropHintBelow = false;
        }
    }

    public void SetDropHint(ListNavItemViewModel target, bool placeBelow)
    {
        foreach (var r in UserLists)
        {
            var isTarget = ReferenceEquals(r, target);
            r.DropHintAbove = isTarget && !placeBelow;
            r.DropHintBelow = isTarget && placeBelow;
        }
    }

    public async Task ReorderAsync(ListNavItemViewModel source, ListNavItemViewModel target, bool placeBelow)
    {
        if (source.Kind != ListKind.User || target.Kind != ListKind.User) return;
        if (ReferenceEquals(source, target)) return;

        MoveWithinCollection(UserLists, source, target, placeBelow);

        var orderedIds = UserLists.Select(i => i.Id["user:".Length..]).ToList();
        await using var ctx = await _dbFactory.CreateDbContextAsync();
        var lists = new ListRepository(ctx);
        await lists.ReorderAsync(orderedIds);
    }

    private static void MoveWithinCollection(
        ObservableCollection<ListNavItemViewModel> coll,
        ListNavItemViewModel source,
        ListNavItemViewModel target,
        bool placeBelow)
    {
        var srcIdx = coll.IndexOf(source);
        var tgtIdx = coll.IndexOf(target);
        if (srcIdx < 0 || tgtIdx < 0 || srcIdx == tgtIdx) return;

        var finalIdx = placeBelow ? tgtIdx + 1 : tgtIdx;
        if (srcIdx < finalIdx) finalIdx--;
        if (finalIdx < 0) finalIdx = 0;
        if (finalIdx >= coll.Count) finalIdx = coll.Count - 1;
        if (finalIdx == srcIdx) return;

        coll.Move(srcIdx, finalIdx);
    }

    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.Name = entity.Name;
            row.WorkingDir = entity.WorkingDir;
            row.DefaultCommitType = entity.DefaultCommitType;
        }
        catch { /* best-effort refresh */ }
    }
}
