using System.ComponentModel;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Ui.Services;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Ui.Tests;

/// <summary>
/// No-op <see cref="IWorkerClient"/> base for tests. Override only the members a
/// test actually exercises; everything else returns a benign default. Keeping the
/// full interface surface here means new interface members only break this file.
/// </summary>
public abstract class StubWorkerClient : IWorkerClient
{
#pragma warning disable CS0067 // interface-mandated events; tests don't raise them
    public event PropertyChangedEventHandler? PropertyChanged;
    public event Action<string, string, DateTime>? TaskStartedEvent;
    public event Action<string, string, string, DateTime>? TaskFinishedEvent;
    public event Action<string>? TaskUpdatedEvent;
    public event Action? ConnectionRestoredEvent;
    public event Action<string>? WorktreeUpdatedEvent;
    public event Action<string>? ListUpdatedEvent;
    public event Action<string, string>? TaskMessageEvent;
    public event Action<WorkerLogEntry>? WorkerLogReceivedEvent;
    public event Action<string, string, string>? TaskQuestionAskedEvent;
    public event Action<string, string>? TaskQuestionResolvedEvent;
    public event Action<string, IReadOnlyList<string>, string>? HandoffRequestedEvent;
    public event Action? NotesUpdatedEvent;
    public event Action? PrepStartedEvent;
    public event Action<string>? PrepLineEvent;
    public event Action<bool>? PrepFinishedEvent;
    public event Action<string>? RefineStartedEvent;
    public event Action<string, bool, string?>? RefineFinishedEvent;
    public event Action<string, string, int, int>? OperationProgressEvent;
    public event Action<string, string, int>? MergeProgressEvent;
    public event Action<string, string>? PlanningMergeStartedEvent;
    public event Action<string, string>? PlanningSubtaskMergedEvent;
    public event Action<string, string, IReadOnlyList<string>, bool>? PlanningMergeConflictEvent;
    public event Action<string, string?>? PlanningMergeAbortedEvent;
    public event Action<string>? PlanningCompletedEvent;
    public event Action<PrimeFiredEvent>? PrimeFired;
    public event Action<UsageSnapshotDto>? UsageUpdatedEvent;
#pragma warning restore CS0067

    public int ClearMyDayCalls { get; private set; }
    public int RunDailyPrepNowCalls { get; private set; }

    public virtual IReadOnlyList<ActiveTaskDto> GetActiveTasks() => System.Array.Empty<ActiveTaskDto>();

    public void RaiseTaskStarted(string slot, string taskId, DateTime startedAt) => TaskStartedEvent?.Invoke(slot, taskId, startedAt);
    public void RaiseTaskFinished(string slot, string taskId, string status, DateTime finishedAt) => TaskFinishedEvent?.Invoke(slot, taskId, status, finishedAt);
    public void RaiseTaskMessage(string taskId, string line) => TaskMessageEvent?.Invoke(taskId, line);
    public void RaiseTaskUpdated(string taskId) => TaskUpdatedEvent?.Invoke(taskId);
    public void RaiseConnectionRestored() => ConnectionRestoredEvent?.Invoke();
    public void RaiseListUpdated(string listId) => ListUpdatedEvent?.Invoke(listId);
    public void RaiseTaskQuestionAsked(string taskId, string questionId, string question) => TaskQuestionAskedEvent?.Invoke(taskId, questionId, question);
    public void RaiseTaskQuestionResolved(string taskId, string questionId) => TaskQuestionResolvedEvent?.Invoke(taskId, questionId);
    public void RaiseHandoffRequested(string taskId, IReadOnlyList<string> survivingTaskIds, string nextPhase = "wait") => HandoffRequestedEvent?.Invoke(taskId, survivingTaskIds, nextPhase);
    public void RaisePlanningMergeConflict(string planningTaskId, string subtaskId, IReadOnlyList<string> files, bool externallyDriven)
        => PlanningMergeConflictEvent?.Invoke(planningTaskId, subtaskId, files, externallyDriven);
    public void RaiseOperationProgress(string opKey, string phase, int current, int total) => OperationProgressEvent?.Invoke(opKey, phase, current, total);
    public void RaiseMergeProgress(string taskId, string phase, int elapsedSeconds) => MergeProgressEvent?.Invoke(taskId, phase, elapsedSeconds);
    public void RaisePlanningMergeStarted(string planningTaskId, string targetBranch) => PlanningMergeStartedEvent?.Invoke(planningTaskId, targetBranch);
    public void RaisePlanningMergeAborted(string planningTaskId, string? reason = null) => PlanningMergeAbortedEvent?.Invoke(planningTaskId, reason);
    public void RaisePlanningCompleted(string planningTaskId) => PlanningCompletedEvent?.Invoke(planningTaskId);

    public void RaisePrepStarted() => PrepStartedEvent?.Invoke();
    public void RaisePrepLine(string line) => PrepLineEvent?.Invoke(line);
    public void RaisePrepFinished(bool ok) => PrepFinishedEvent?.Invoke(ok);

    public virtual bool IsConnected => false;
    public virtual bool IsReconnecting => false;
    public virtual string? LastApproveTarget => null;

    public virtual Task WakeQueueAsync() => Task.CompletedTask;
    public virtual Task RunNowAsync(string taskId) => Task.CompletedTask;
    public virtual Task ContinueTaskAsync(string taskId, string followUpPrompt) => Task.CompletedTask;
    public (string TaskId, string QuestionId, string Answer)? LastAnswer { get; private set; }
    public virtual Task AnswerTaskQuestionAsync(string taskId, string questionId, string answer)
    {
        LastAnswer = (taskId, questionId, answer);
        return Task.CompletedTask;
    }
    public PendingQuestionDto? PendingQuestion;
    public virtual Task<PendingQuestionDto?> GetPendingQuestionAsync(string taskId) => Task.FromResult(PendingQuestion);
    public virtual Task ResetTaskAsync(string taskId) => Task.CompletedTask;
    public virtual Task CancelTaskAsync(string taskId) => Task.CompletedTask;
    public virtual Task<(bool Ok, string? Error)> DeleteTaskAsync(string taskId) => Task.FromResult<(bool, string?)>((true, null));
    public virtual Task<List<AgentInfo>> GetAgentsAsync() => Task.FromResult(new List<AgentInfo>());
    public virtual Task RefreshAgentsAsync() => Task.CompletedTask;
    public virtual Task<SeedResultDto?> RestoreDefaultAgentsAsync() => Task.FromResult<SeedResultDto?>(null);
    public virtual Task<ListConfigDto?> GetListConfigAsync(string listId) => Task.FromResult<ListConfigDto?>(null);
    public virtual Task<List<string>> GetRepoImportFoldersAsync() => Task.FromResult(new List<string>());
    public virtual Task SetRepoImportFoldersAsync(List<string> folders) => Task.CompletedTask;
    public virtual Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto) => Task.CompletedTask;
    public virtual Task<List<SessionSkillDto>> GetSessionSkillsAsync() => Task.FromResult(new List<SessionSkillDto>());
    public virtual Task<List<string>> InstallSessionSkillAsync(string url) => Task.FromResult(new List<string>());
    public virtual Task UpdateSessionSkillAsync(string sourceUrl) => Task.CompletedTask;
    public virtual Task RemoveSessionSkillAsync(string sourceUrl) => Task.CompletedTask;
    public virtual Task<BaseDirtyWarningDto?> SetTaskStatusAsync(string taskId, TaskStatus status) => Task.FromResult<BaseDirtyWarningDto?>(null);
    public virtual Task SetTaskDoneAsync(string taskId) => Task.CompletedTask;
    public virtual Task UnsetTaskDoneAsync(string taskId) => Task.CompletedTask;
    public virtual Task DequeueTaskAsync(string taskId) => Task.CompletedTask;
    public virtual Task<MergeResultDto?> ApproveReviewAsync(string taskId, string targetBranch) => Task.FromResult<MergeResultDto?>(null);
    public virtual Task<MergePreviewDto?> PreviewMergeAsync(string taskId, string targetBranch) => Task.FromResult<MergePreviewDto?>(null);
    public virtual Task<MergeResultDto> MergeTaskAsync(string taskId, string targetBranch, bool removeWorktree, string commitMessage) => Task.FromResult(new MergeResultDto("merged", System.Array.Empty<string>(), null));
    public virtual Task RejectReviewToQueueAsync(string taskId, string feedback) => Task.CompletedTask;
    public virtual Task RejectReviewToIdleAsync(string taskId) => Task.CompletedTask;
    public virtual Task CancelReviewAsync(string taskId) => Task.CompletedTask;
    public virtual Task<MergeResultDto> StartConflictMergeAsync(string taskId, string targetBranch) => Task.FromResult(new MergeResultDto("conflict", System.Array.Empty<string>(), null));
    public virtual Task<MergeConflictDocumentsDto> GetMergeConflictDocumentsAsync(string taskId) => Task.FromResult(new MergeConflictDocumentsDto(taskId, System.Array.Empty<ConflictDocumentDto>()));
    public virtual Task WriteConflictResolutionAsync(string taskId, string path, string resolvedContent) => Task.CompletedTask;
    public virtual Task<MergeResultDto> ContinueConflictMergeAsync(string taskId) => Task.FromResult(new MergeResultDto("merged", System.Array.Empty<string>(), null));
    public virtual Task AbortConflictMergeAsync(string taskId) => Task.CompletedTask;
    public virtual Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
    public virtual Task SubmitTaskForReviewAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
    public virtual Task<LaunchSpec> GetInteractiveLaunchSpecAsync(string taskId, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task<LaunchSpec> GetAdHocLaunchSpecAsync(string directory, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(directory, "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task<LaunchSpec> GetMergeHelperLaunchSpecAsync(IReadOnlyList<string> taskIds, string listId, string handlerTaskId, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task<string> CreateMergeHelperTaskAsync(
        IReadOnlyList<string> taskIds, string listId, string title, string descriptionHeader, CancellationToken ct = default)
        => Task.FromResult(Guid.NewGuid().ToString());
    public virtual Task<LaunchSpec> GetMergeHelperHandoffLaunchSpecAsync(
        string taskId, IReadOnlyList<string> survivingTaskIds, string nextPhase, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task<LaunchSpec> GetPlanningStartLaunchSpecAsync(string taskId, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task<LaunchSpec> GetPlanningResumeLaunchSpecAsync(string taskId, CancellationToken ct = default)
        => Task.FromResult(new LaunchSpec(".", "claude", Array.Empty<string>(), new Dictionary<string, string>()));
    public virtual Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;
    public virtual Task<DiscardPlanningOutcome> DiscardPlanningSessionAsync(string taskId, bool dequeueQueuedChildren = false, CancellationToken ct = default)
        => Task.FromResult(new DiscardPlanningOutcome(DiscardPlanningResult.Discarded, 0, 0));
    public virtual Task FinalizePlanningSessionAsync(string taskId, bool queueAgentTasks = true, CancellationToken ct = default) => Task.CompletedTask;
    public virtual Task<int> GetPendingDraftCountAsync(string taskId, CancellationToken ct = default) => Task.FromResult(0);
    public virtual Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId) => Task.FromResult<MergeTargetsDto?>(null);
    public virtual Task<IReadOnlyList<WorkerLogEntry>> GetRecentLogsAsync() => Task.FromResult<IReadOnlyList<WorkerLogEntry>>(Array.Empty<WorkerLogEntry>());
    public virtual Task<IReadOnlyList<SubtaskDiffDto>> GetPlanningAggregateAsync(string planningTaskId)
        => Task.FromResult<IReadOnlyList<SubtaskDiffDto>>(Array.Empty<SubtaskDiffDto>());
    public virtual Task<CombinedDiffResultDto?> BuildPlanningIntegrationBranchAsync(string planningTaskId, string targetBranch)
        => Task.FromResult<CombinedDiffResultDto?>(null);
    public virtual Task ContinuePlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
    public virtual Task AbortPlanningMergeAsync(string planningTaskId) => Task.CompletedTask;
    public virtual Task<IReadOnlyList<PlanningMergeConflictStateDto>> GetActiveExternalPlanningMergeConflictsAsync()
        => Task.FromResult<IReadOnlyList<PlanningMergeConflictStateDto>>(Array.Empty<PlanningMergeConflictStateDto>());
    public virtual Task QueuePlanningSubtasksAsync(string parentTaskId, CancellationToken ct = default) => Task.CompletedTask;
    public virtual Task<string?> GetWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult<string?>(null);
    public virtual Task<string> GenerateWeekReportAsync(DateOnly start, DateOnly end) => Task.FromResult("");
    public virtual Task<bool> RunDailyPrepNowAsync() { RunDailyPrepNowCalls++; return Task.FromResult(false); }
    public virtual Task ClearMyDayAsync() { ClearMyDayCalls++; return Task.CompletedTask; }
    public virtual Task<AppSettingsDto?> GetAppSettingsAsync() => Task.FromResult<AppSettingsDto?>(null);
    public virtual Task UpdateAppSettingsAsync(AppSettingsDto dto) => Task.CompletedTask;
    public virtual Task<List<PrimeScheduleDto>> GetPrimeSchedulesAsync() => Task.FromResult(new List<PrimeScheduleDto>());
    public virtual Task<PrimeScheduleDto?> UpsertPrimeScheduleAsync(PrimeScheduleDto dto) => Task.FromResult<PrimeScheduleDto?>(null);
    public virtual Task DeletePrimeScheduleAsync(Guid id) => Task.CompletedTask;
    public virtual Task UpdateListAsync(UpdateListDto dto) => Task.CompletedTask;
    public virtual Task UpdateListConfigAsync(UpdateListConfigDto dto) => Task.CompletedTask;
    public virtual Task<WorktreeCleanupDto?> CleanupFinishedWorktreesAsync(string? listId = null) => Task.FromResult<WorktreeCleanupDto?>(null);
    public virtual Task<WorktreeResetDto?> ResetAllWorktreesAsync() => Task.FromResult<WorktreeResetDto?>(null);
    public virtual Task<List<WorktreeOverviewDto>> GetWorktreesOverviewAsync(string? listId) => Task.FromResult(new List<WorktreeOverviewDto>());
    public virtual Task<(bool Ok, string? Error)> SetWorktreeStateAsync(string taskId, WorktreeState newState) => Task.FromResult((true, (string?)null));
    public virtual Task<ForceRemoveResultDto?> ForceRemoveWorktreeAsync(string taskId) => Task.FromResult<ForceRemoveResultDto?>(null);
    public virtual Task<List<DailyNoteDto>> GetDailyNotesAsync(DateOnly day) => Task.FromResult(new List<DailyNoteDto>());
    public virtual Task<List<DailyNoteDto>> GetDailyNotesBetweenAsync(DateOnly start, DateOnly end) => Task.FromResult(new List<DailyNoteDto>());
    public virtual Task<DailyNoteDto?> AddDailyNoteAsync(DateOnly day, string text) => Task.FromResult<DailyNoteDto?>(null);
    public virtual Task UpdateDailyNoteAsync(string id, string text) => Task.CompletedTask;
    public virtual Task DeleteDailyNoteAsync(string id) => Task.CompletedTask;
    public string LastPrepLog = "";
    public virtual Task<string> GetLastPrepLogAsync() => Task.FromResult(LastPrepLog);
    public WorkerBuildInfoDto? WorkerBuildInfo;
    public virtual Task<WorkerBuildInfoDto?> GetWorkerBuildInfoAsync() => Task.FromResult(WorkerBuildInfo);
    public virtual Task RefineTaskAsync(string taskId) => Task.CompletedTask;

    public virtual Task<ClaudeBinDto?> GetClaudeBinAsync() => Task.FromResult<ClaudeBinDto?>(null);
    public virtual Task<ClaudeBinDto?> SetClaudeBinAsync(string? value) => Task.FromResult<ClaudeBinDto?>(null);
    public virtual Task<OnlineInboxStateDto?> GetOnlineInboxStateAsync() => Task.FromResult<OnlineInboxStateDto?>(null);
    public virtual Task SetOnlineInboxConfigAsync(OnlineInboxConfigInputDto input) => Task.CompletedTask;
    public virtual Task SetOnlineInboxAuthAsync(string refreshToken) => Task.CompletedTask;
    public virtual Task ClearOnlineInboxAuthAsync() => Task.CompletedTask;

    public virtual Task<UsageSnapshotDto?> GetUsageSnapshotAsync() => Task.FromResult<UsageSnapshotDto?>(null);
    public virtual Task<UsageSnapshotDto?> RefreshUsageAsync() => Task.FromResult<UsageSnapshotDto?>(null);
    public virtual Task<IReadOnlyList<ModelUsageRowDto>> GetModelUsageAsync(DateOnly from, DateOnly to)
        => Task.FromResult<IReadOnlyList<ModelUsageRowDto>>(Array.Empty<ModelUsageRowDto>());
    public virtual Task<IReadOnlyList<TaskUsageRowDto>> GetTaskUsageAsync(DateOnly from, DateOnly to)
        => Task.FromResult<IReadOnlyList<TaskUsageRowDto>>(Array.Empty<TaskUsageRowDto>());

    // "Not installed, but Node is fine" — the state that exercises the hint card and the
    // install button without ever touching a real CLI.
    public virtual Task<TokenTrackerStatusDto?> GetTokenTrackerStatusAsync() =>
        Task.FromResult<TokenTrackerStatusDto?>(new TokenTrackerStatusDto(
            Installed: false, Version: null, NodeOk: true, NodeVersion: "v22.0.0",
            LastFetchedUtc: null, LastError: null, FormatVersion: null, SessionCount: 0));

    public virtual Task<TokenTrackerStatusDto?> RefreshTokenTrackerAsync() => GetTokenTrackerStatusAsync();

    public virtual Task<TokenTrackerStatusDto?> InstallTokenTrackerAsync() => GetTokenTrackerStatusAsync();

    public virtual Task<TokenTrackerDashboardDto?> OpenTokenTrackerDashboardAsync() =>
        Task.FromResult<TokenTrackerDashboardDto?>(new TokenTrackerDashboardDto(true, "http://127.0.0.1:7681/", null));

    public virtual Task<TicketSettingsDto?> GetTicketSettingsAsync() => Task.FromResult<TicketSettingsDto?>(null);
    public virtual Task SetTicketApiBaseUrlAsync(string? baseUrl) => Task.CompletedTask;
    public virtual Task SetTicketTokenAsync(string token) => Task.CompletedTask;
    public virtual Task ClearTicketTokenAsync() => Task.CompletedTask;
    public virtual Task<TicketConnectionDto?> TestTicketConnectionAsync() => Task.FromResult<TicketConnectionDto?>(null);
    public virtual Task<List<TicketProjectDto>> GetTicketProjectsAsync() => Task.FromResult(new List<TicketProjectDto>());
    public virtual Task<TicketImportResultDto> ImportTicketsAsync(string listId) => Task.FromResult(new TicketImportResultDto(0, 0));

    public void RaiseUsageUpdated(UsageSnapshotDto snapshot) => UsageUpdatedEvent?.Invoke(snapshot);

    protected void RaisePropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
