using Avalonia.Threading;
using System;
using System.Threading;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Islands;
using ClaudeDo.Ui.ViewModels.Modals;
using ClaudeDo.Ui.ViewModels.Planning;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Ui.ViewModels;

public sealed partial class IslandsShellViewModel : ViewModelBase
{
    public ListsIslandViewModel? Lists { get; }
    public TasksIslandViewModel? Tasks { get; }
    public DetailsIslandViewModel? Details { get; }
    public WorkerClient? Worker { get; }
    public UpdateCheckService UpdateCheck => _updateCheck;

    public string ConnectionText =>
        Worker?.IsConnected == true ? "Online"
        : Worker?.IsReconnecting == true ? "Connecting…"
        : "Offline";

    public bool IsOffline => Worker?.IsConnected != true && Worker?.IsReconnecting != true;

    private readonly UpdateCheckService _updateCheck = null!;
    private readonly InstallerLocator _installerLocator = null!;
    private readonly IDbContextFactory<ClaudeDoDbContext>? _dbFactory;
    private readonly Func<WorktreesOverviewModalViewModel> _worktreesOverviewVmFactory = () => null!;
    private readonly Func<MergeModalViewModel> _mergeVmFactory = () => null!;

    public Func<MergeModalViewModel> ResolveMergeVm => _mergeVmFactory;

    // Set by MainWindow to open the conflict resolution dialog.
    public Func<ConflictResolutionViewModel, Task>? ShowConflictDialog { get; set; }

    // Set by MainWindow to open the About dialog.
    public Func<AboutModalViewModel, Task>? ShowAboutModal { get; set; }

    // Set by MainWindow to open the global worktrees overview dialog.
    public Func<WorktreesOverviewModalViewModel, Task>? ShowWorktreesOverviewModal { get; set; }

    [ObservableProperty] private bool _isUpdateBannerVisible;
    [ObservableProperty] private string? _updateBannerLatestVersion;
    [ObservableProperty] private string? _inlineUpdateStatus;
    private bool _bannerDismissedThisSession;

    [ObservableProperty]
    private double _windowWidth = 1280;

    [ObservableProperty]
    private string? _workerLogText;

    [ObservableProperty]
    private WorkerLogLevel _workerLogLevel;

    [ObservableProperty]
    private bool _isWorkerLogVisible;

    public bool ShowDetails => WindowWidth >= 1100;
    public bool ShowLists => WindowWidth >= 780;

    private readonly System.Timers.Timer _clearTimer = new(30_000) { AutoReset = false };

    [ObservableProperty] private string? _primeStatus;
    private readonly System.Timers.Timer _primeStatusTimer = new(5_000) { AutoReset = false };

    [RelayCommand]
    private void FocusSearch() => Lists?.RequestFocusSearch();

    [RelayCommand]
    private void FocusAddTask() => Tasks?.RequestFocusAddTask();

    public async Task ToggleSelectedDoneAsync()
    {
        if (Tasks?.SelectedTask is { } row)
            await Tasks.ToggleDoneCommand.ExecuteAsync(row);
    }

    partial void OnWindowWidthChanged(double value)
    {
        OnPropertyChanged(nameof(ShowDetails));
        OnPropertyChanged(nameof(ShowLists));
    }

    public void OnWorkerLogReceived(WorkerLogEntry entry)
    {
        var hhmm = entry.TimestampUtc.ToLocalTime().ToString("HH:mm");
        WorkerLogText = $"{hhmm} · {entry.Message}";
        WorkerLogLevel = entry.Level;
        IsWorkerLogVisible = true;
        _clearTimer.Stop();
        _clearTimer.Start();
    }

    public void ClearWorkerLog()
    {
        IsWorkerLogVisible = false;
        WorkerLogText = null;
    }

    private void OnPrimeFired(PrimeFiredEvent evt)
    {
        var when = evt.FiredAt.LocalDateTime.ToString("HH:mm");
        PrimeStatus = evt.Success
            ? $"✓ Primed Claude at {when}"
            : $"⚠ Prime failed: {evt.Message}";
        _primeStatusTimer.Stop();
        _primeStatusTimer.Start();
    }

    private void OnPlanningMergeConflict(string planningTaskId, string subtaskId, IReadOnlyList<string> conflictedFiles)
    {
        // Already on UI thread (WorkerClient dispatches via Dispatcher.UIThread.Post).
        _ = OpenConflictDialogAsync(planningTaskId, subtaskId, conflictedFiles);
    }

    private async Task OpenConflictDialogAsync(string planningTaskId, string subtaskId, IReadOnlyList<string> conflictedFiles)
    {
        if (ShowConflictDialog == null || _dbFactory == null) return;

        string subtaskTitle = subtaskId;
        string worktreePath = System.Environment.CurrentDirectory;
        string targetBranch = Worker?.LastMergeAllTarget ?? "main";

        try
        {
            await using var ctx = await _dbFactory.CreateDbContextAsync();
            var entity = await ctx.Tasks
                .Include(t => t.Worktree)
                .AsNoTracking()
                .FirstOrDefaultAsync(t => t.Id == subtaskId);
            if (entity != null)
            {
                subtaskTitle = entity.Title;
                if (entity.Worktree?.Path is { } p)
                    worktreePath = p;
            }
        }
        catch { /* Non-fatal: fall back to subtaskId and cwd */ }

        var vm = new ConflictResolutionViewModel(
            Worker!,
            planningTaskId,
            subtaskTitle,
            targetBranch,
            conflictedFiles,
            worktreePath);

        await ShowConflictDialog(vm);
    }

    // For tests only — does NOT wire up events.
    internal IslandsShellViewModel() { }

    public IslandsShellViewModel(
        ListsIslandViewModel lists,
        TasksIslandViewModel tasks,
        DetailsIslandViewModel details,
        WorkerClient worker,
        UpdateCheckService updateCheck,
        InstallerLocator installerLocator,
        IDbContextFactory<ClaudeDoDbContext> dbFactory,
        Func<WorktreesOverviewModalViewModel> worktreesOverviewVmFactory,
        Func<MergeModalViewModel> mergeVmFactory)
    {
        Lists = lists; Tasks = tasks; Details = details; Worker = worker;
        _updateCheck = updateCheck;
        _installerLocator = installerLocator;
        _dbFactory = dbFactory;
        _worktreesOverviewVmFactory = worktreesOverviewVmFactory;
        _mergeVmFactory = mergeVmFactory;
        Lists.SelectionChanged += (_, _) => Tasks.LoadForList(Lists.SelectedList);
        Tasks.SelectionChanged += (_, _) => Details.Bind(Tasks.SelectedTask);
        Tasks.TasksChanged     += (_, _) => _ = Lists.RefreshCountsAsync();
        Tasks.OpenListSettingsRequested += (_, _) =>
        {
            if (Lists.SelectedList is { } row)
                Lists.OpenListSettingsCommand.Execute(row);
        };
        Details.CloseDetail = () => Tasks.SelectedTask = null;
        Details.DeleteFromList = row =>
        {
            Tasks.LoadForList(Lists.SelectedList);
            _ = Lists.RefreshCountsAsync();
            return System.Threading.Tasks.Task.CompletedTask;
        };
        Worker.PropertyChanged += (_, e) =>
        {
            if (e.PropertyName is nameof(WorkerClient.IsConnected) or nameof(WorkerClient.IsReconnecting))
            {
                OnPropertyChanged(nameof(ConnectionText));
                OnPropertyChanged(nameof(IsOffline));
            }
        };
        Worker.WorkerLogReceivedEvent += OnWorkerLogReceived;
        Worker.PlanningMergeConflictEvent += OnPlanningMergeConflict;
        Worker.PrimeFired += OnPrimeFired;
        _clearTimer.Elapsed += (_, _) =>
        {
            if (Dispatcher.UIThread.CheckAccess())
                ClearWorkerLog();
            else
                Dispatcher.UIThread.Post(ClearWorkerLog);
        };
        _primeStatusTimer.Elapsed += (_, _) =>
            Avalonia.Threading.Dispatcher.UIThread.Post(() => PrimeStatus = null);
        _ = Lists.LoadAsync();
        _updateCheck.PropertyChanged += (_, e) =>
        {
            if (e.PropertyName == nameof(UpdateCheckService.LastCheckStatus))
            {
                RefreshBannerFromStatus();
            }
        };
        // Fire-and-forget startup check — never block UI.
        _ = Task.Run(async () =>
        {
            try { await _updateCheck.CheckNowAsync(CancellationToken.None); } catch { }
        });
    }

    private void RefreshBannerFromStatus()
    {
        switch (_updateCheck.LastCheckStatus)
        {
            case UpdateCheckStatus.UpdateAvailable:
                if (_bannerDismissedThisSession) { IsUpdateBannerVisible = false; break; }
                UpdateBannerLatestVersion = _updateCheck.LatestVersion;
                IsUpdateBannerVisible = true;
                InlineUpdateStatus = null;
                break;
            case UpdateCheckStatus.UpToDate:
                IsUpdateBannerVisible = false;
                ShowInlineStatus($"You're up to date (v{_updateCheck.CurrentVersion})");
                break;
            case UpdateCheckStatus.CheckFailed:
                ShowInlineStatus("Could not check for updates");
                break;
        }
    }

    private async void ShowInlineStatus(string text)
    {
        InlineUpdateStatus = text;
        await Task.Delay(3000);
        if (InlineUpdateStatus == text) InlineUpdateStatus = null;
    }

    [RelayCommand]
    private async Task OpenAbout()
    {
        var vm = new AboutModalViewModel();
        if (ShowAboutModal is not null) await ShowAboutModal(vm);
    }

    private bool _worktreesOverviewOpen;

    [RelayCommand]
    private async Task OpenWorktreesOverviewGlobalAsync()
    {
        if (ShowWorktreesOverviewModal is null || _worktreesOverviewOpen) return;
        _worktreesOverviewOpen = true;
        try
        {
            var vm = _worktreesOverviewVmFactory();
            vm.Configure(null, null);
            await vm.LoadAsync();
            await ShowWorktreesOverviewModal(vm);
        }
        finally { _worktreesOverviewOpen = false; }
    }

    [RelayCommand]
    private async Task CheckForUpdatesAsync()
    {
        await _updateCheck.CheckNowAsync(CancellationToken.None);
    }

    [ObservableProperty] private string? _restartWorkerStatus;

    [RelayCommand]
    private async Task RestartWorkerAsync()
    {
        if (!OperatingSystem.IsWindows())
        {
            await FlashRestartStatusAsync("Service control is Windows-only.");
            return;
        }

        RestartWorkerStatus = "Restarting worker…";
        try
        {
            await Task.Run(RestartWorkerService);
            await FlashRestartStatusAsync("Worker restarted.");
        }
        catch (InvalidOperationException)
        {
            // ServiceController throws this when the service is not installed.
            await FlashRestartStatusAsync("ClaudeDoWorker service is not installed.");
        }
        catch (Exception ex)
        {
            await FlashRestartStatusAsync($"Restart failed: {ex.Message}");
        }
    }

    [System.Runtime.Versioning.SupportedOSPlatform("windows")]
    private static void RestartWorkerService()
    {
        using var sc = new System.ServiceProcess.ServiceController("ClaudeDoWorker");
        if (sc.Status != System.ServiceProcess.ServiceControllerStatus.Stopped)
        {
            sc.Stop();
            sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(20));
        }
        sc.Start();
        sc.WaitForStatus(System.ServiceProcess.ServiceControllerStatus.Running, TimeSpan.FromSeconds(20));
    }

    private async Task FlashRestartStatusAsync(string text)
    {
        RestartWorkerStatus = text;
        await Task.Delay(3000);
        if (RestartWorkerStatus == text) RestartWorkerStatus = null;
    }

    [RelayCommand]
    private void DismissBanner()
    {
        _bannerDismissedThisSession = true;
        IsUpdateBannerVisible = false;
    }

    [RelayCommand]
    private void UpdateNow()
    {
        var path = _installerLocator.Find();
        if (path is null) return;

        try
        {
            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(path) { UseShellExecute = true });
            Environment.Exit(0);
        }
        catch
        {
            // Intentionally silent — if this fails there's nothing useful to show.
        }
    }
}
