using System.Collections.ObjectModel;
using System.ComponentModel;
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Ui.ViewModels.Modals;

public sealed partial class RepoImportItemViewModel : ViewModelBase
{
    public string Name { get; init; } = "";
    public string FullPath { get; init; } = "";

    // True when a list already points at this path. Such rows are shown ticked + disabled.
    public bool AlreadyAdded { get; init; }
    public bool CanToggle => !AlreadyAdded;

    [ObservableProperty] private bool _isChecked;

    // Driven by the search filter; the row collapses when it doesn't match.
    [ObservableProperty] private bool _isVisible = true;
}

public sealed partial class RepoImportModalViewModel : ViewModelBase
{
    private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
    private readonly IWorkerClient _workerClient;
    private readonly HashSet<string> _existingDirs = new(StringComparer.OrdinalIgnoreCase);
    private readonly List<string> _folders = new();

    public OperationStatus ScanOp { get; } = new();

    public ObservableCollection<RepoImportItemViewModel> Repos { get; } = new();

    public Action? CloseAction { get; set; }
    public event Action<string>? ErrorReported;

    [ObservableProperty] private string _searchText = "";

    public int CreateCount => Repos.Count(r => r.IsChecked && !r.AlreadyAdded);
    public bool CanCreate => CreateCount > 0;
    public string CreateButtonText => $"Create {CreateCount} list(s)";
    public bool HasFolders => _folders.Count > 0;

    public RepoImportModalViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IWorkerClient workerClient)
    {
        _dbFactory = dbFactory;
        _workerClient = workerClient;
    }

    public async Task LoadAsync(CancellationToken ct = default)
    {
        ClearRepos();
        _existingDirs.Clear();
        _folders.Clear();

        await using var ctx = await _dbFactory.CreateDbContextAsync(ct);
        var lists = new ListRepository(ctx);
        foreach (var l in await lists.GetAllAsync(ct))
        {
            if (!string.IsNullOrWhiteSpace(l.WorkingDir))
                _existingDirs.Add(l.WorkingDir!);
        }

        try
        {
            foreach (var f in await _workerClient.GetRepoImportFoldersAsync())
                AddFolderToSet(f);
        }
        catch (Exception ex)
        {
            ErrorReported?.Invoke(Loc.T("vm.repoImport.loadFailed", ex.Message));
        }

        await ScanAndAddAsync(_folders);
        OnPropertyChanged(nameof(HasFolders));
        NotifyCreateState();
    }

    public async Task AddFoldersAsync(IEnumerable<string> folders)
    {
        var added = new List<string>();
        foreach (var f in folders)
            if (AddFolderToSet(f)) added.Add(f);

        if (added.Count == 0) return;

        await ScanAndAddAsync(added);
        OnPropertyChanged(nameof(HasFolders));
        NotifyCreateState();
        await SaveFoldersAsync();
    }

    private async Task ScanAndAddAsync(IEnumerable<string> folders)
    {
        using var op = ScanOp.Begin(Loc.T("ops.repoImport.scanning"));
        var current = new HashSet<string>(
            Repos.Select(r => r.FullPath), StringComparer.OrdinalIgnoreCase);

        foreach (var folder in folders)
        {
            var found = await Task.Run(() => RepoScanner.Scan(folder));
            foreach (var item in BuildCandidates(found, current, _existingDirs))
            {
                item.PropertyChanged += OnItemChanged;
                Repos.Add(item);
                current.Add(item.FullPath);
            }
        }
        ApplyFilter();
    }

    public static List<RepoImportItemViewModel> BuildCandidates(
        IEnumerable<RepoCandidate> found,
        IReadOnlySet<string> currentPaths,
        IReadOnlySet<string> existingDirs)
    {
        var items = new List<RepoImportItemViewModel>();
        foreach (var c in found)
        {
            if (currentPaths.Contains(c.FullPath)) continue;
            var alreadyAdded = existingDirs.Contains(c.FullPath);
            items.Add(new RepoImportItemViewModel
            {
                Name = c.Name,
                FullPath = c.FullPath,
                AlreadyAdded = alreadyAdded,
                // New repos start unchecked (no auto-select); already-added rows show ticked + disabled.
                IsChecked = alreadyAdded,
            });
        }
        return items;
    }

    [RelayCommand]
    private async Task ForgetFoldersAsync()
    {
        _folders.Clear();
        ClearRepos();
        OnPropertyChanged(nameof(HasFolders));
        NotifyCreateState();
        await SaveFoldersAsync();
    }

    partial void OnSearchTextChanged(string value) => ApplyFilter();

    private void ApplyFilter()
    {
        var q = SearchText?.Trim() ?? "";
        foreach (var r in Repos)
            r.IsVisible = q.Length == 0
                || r.Name.Contains(q, StringComparison.OrdinalIgnoreCase)
                || r.FullPath.Contains(q, StringComparison.OrdinalIgnoreCase);
    }

    private bool AddFolderToSet(string folder)
    {
        if (string.IsNullOrWhiteSpace(folder)) return false;
        if (_folders.Any(f => string.Equals(f, folder, StringComparison.OrdinalIgnoreCase)))
            return false;
        _folders.Add(folder);
        return true;
    }

    private async Task SaveFoldersAsync()
    {
        try
        {
            await _workerClient.SetRepoImportFoldersAsync(_folders);
        }
        catch (Exception ex)
        {
            ErrorReported?.Invoke(Loc.T("vm.repoImport.saveFailed", ex.Message));
        }
    }

    private void ClearRepos()
    {
        foreach (var r in Repos) r.PropertyChanged -= OnItemChanged;
        Repos.Clear();
    }

    private void OnItemChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(RepoImportItemViewModel.IsChecked))
            NotifyCreateState();
    }

    private void NotifyCreateState()
    {
        OnPropertyChanged(nameof(CreateCount));
        OnPropertyChanged(nameof(CanCreate));
        OnPropertyChanged(nameof(CreateButtonText));
    }

    [RelayCommand]
    private async Task CreateAsync()
    {
        var toCreate = Repos.Where(r => r.IsChecked && !r.AlreadyAdded).ToList();
        if (toCreate.Count > 0)
        {
            await using var ctx = await _dbFactory.CreateDbContextAsync();
            var lists = new ListRepository(ctx);
            foreach (var r in toCreate)
            {
                await lists.AddAsync(new ListEntity
                {
                    Id = Guid.NewGuid().ToString("N"),
                    Name = r.Name,
                    WorkingDir = r.FullPath,
                    DefaultCommitType = CommitTypeRegistry.DefaultType,
                    CreatedAt = DateTime.UtcNow,
                });
            }
        }
        CloseAction?.Invoke();
    }

    [RelayCommand]
    private void Cancel() => CloseAction?.Invoke();
}
