using ClaudeDo.Data.Models;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Data.Wire;

// The SignalR contract between WorkerHub (server) and WorkerClient (client). Both used to
// declare their own copy of every record in this file; they drifted (MergePreviewDto lost its
// verify fields on the client side, two records disagreed on their name). One definition, so
// a hub signature change is a compile error instead of a silently dropped JSON field.
//
// Imported project-wide via a <Using Include="ClaudeDo.Data.Wire" /> item in the Ui, Worker
// and their test projects — that's why nothing here needs a per-file using.

public record ActiveTaskDto(string Slot, string TaskId, DateTime StartedAt);

public record WorkerBuildInfoDto(string? BuildSha);

public record AppSettingsDto(
    string DefaultClaudeInstructions,
    string DefaultModel,
    int DefaultMaxTurns,
    string DefaultPermissionMode,
    int MaxParallelExecutions,
    string WorktreeStrategy,
    string? CentralWorktreeRoot,
    bool WorktreeAutoCleanupEnabled,
    int WorktreeAutoCleanupDays,
    string? ReportExcludedPaths,
    int StandupWeekday,
    int DailyPrepMaxTasks,
    List<string>? SessionSkills = null,
    List<ModelPresetDto>? ModelPresets = null,
    int UsageGateFiveHourPct = 80,
    int UsageGateSevenDayPct = 90,
    int MaxTurnsCeiling = 80,
    // Throttle stages per bucket — dragged on the usage-monitor gauges, not typed in Settings.
    int UsageThrottleFiveHourSoftPct = 50,
    int UsageThrottleFiveHourHardPct = 65,
    int UsageThrottleSevenDaySoftPct = 50,
    int UsageThrottleSevenDayHardPct = 65,
    bool AutoContinueOnUsageLimit = false);

public record ModelPresetDto(string Model, string Effort, int MaxTurns);

public record SessionSkillDto(
    string Name,
    string Description,
    string SourceUrl,
    string PinnedRef,
    DateTimeOffset AddedAt);

public record WorktreeCleanupDto(int Removed);

public record WorktreeResetDto(int Removed, int TasksAffected, bool Blocked, int RunningTasks);

public record WorktreeOverviewDto(
    string TaskId,
    string TaskTitle,
    TaskStatus TaskStatus,
    string ListId,
    string ListName,
    string Path,
    string BranchName,
    string BaseCommit,
    WorktreeState State,
    string? DiffStat,
    DateTime CreatedAt,
    bool PathExistsOnDisk);

public record ForceRemoveResultDto(bool Removed, string? Reason);

public record PlanningMergeConflictStateDto(string PlanningTaskId, string SubtaskId);

public record PendingQuestionDto(string TaskId, string QuestionId, string Question);

public record MergeResultDto(string Status, IReadOnlyList<string> ConflictFiles, string? ErrorMessage);

public record BaseDirtyWarningDto(int ModifiedCount, int UntrackedCount);

public record SetTaskStatusResultDto(BaseDirtyWarningDto? BaseDirty);

public record MergePreviewDto(
    string Status, IReadOnlyList<string> ConflictFiles, int ChangedFileCount,
    int? VerifyExitCode = null, long? VerifyDurationMs = null, string? VerifyOutputTail = null);

public record MergeTargetsDto(
    string DefaultBranch, IReadOnlyList<string> LocalBranches, string DefaultCommitMessage);

public record MergeConflictDocumentsDto(string TaskId, IReadOnlyList<ConflictDocumentDto> Files);

public record ConflictDocumentDto(string Path, bool IsBinary, IReadOnlyList<MergeSegmentDto> Segments);

public record MergeSegmentDto(bool IsConflict, string Text, string Ours, string? Base, string Theirs);

public record UpdateListDto(string Id, string Name, string? WorkingDir, string DefaultCommitType, bool IsManual = false, bool FindingsTracked = false, string? Icon = null);

// TicketProjectId is tri-state on purpose: a caller that doesn't own the field (anything but the
// list-settings modal) must not be able to clear it by omission — SetConfigAsync copies the entity
// verbatim. Its scale: null = keep stored, <= 0 = clear the link, > 0 = set it.
public record UpdateListConfigDto(string ListId, string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns = null, List<string>? SessionSkills = null, string? VerifyCommand = null, string? PermissionMode = null, int? TicketProjectId = null);

public record UpdateTaskAgentSettingsDto(string TaskId, string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns = null, List<string>? SessionSkills = null, string? PermissionMode = null);

public record ListConfigDto(string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns = null, List<string>? SessionSkills = null, string? VerifyCommand = null, string? PermissionMode = null, int? TicketProjectId = null);

public record SeedResultDto(int Copied, int Skipped);

/// <param name="Value">What worker.config.json holds (a command name or a full path).</param>
/// <param name="ResolvedPath">Where it actually resolves to right now, or null if nothing resolves —
/// the whole point of surfacing this setting is telling those two cases apart.</param>
public record ClaudeBinDto(string Value, string? ResolvedPath);

public record OnlineInboxStateDto(
    bool Enabled,
    string ApiBaseUrl,
    string Authority,
    string ClientId,
    string Scopes,
    string RedirectUri,
    bool SignedIn,
    int PollIntervalSeconds);

public record OnlineInboxConfigInputDto(
    bool Enabled,
    string ApiBaseUrl,
    int PollIntervalSeconds,
    string Authority,
    string ClientId,
    string Scopes,
    string RedirectUri);

public record UsageLimitDto(
    string Kind,
    string Group,
    double Percent,
    string Severity,
    DateTimeOffset? ResetsAt,
    string? ScopeModelDisplayName,
    bool IsActive);

public record UsageSnapshotDto(
    double? FiveHourPercent,
    DateTimeOffset? FiveHourResetsAt,
    double? SevenDayPercent,
    DateTimeOffset? SevenDayResetsAt,
    IReadOnlyList<UsageLimitDto> Limits,
    int FiveHourThresholdPct,
    int SevenDayThresholdPct,
    bool IsGateBlocked,
    string? GateReason,
    DateTime? FetchedAtUtc,
    bool IsStale,
    string? LastError,
    int ConfiguredSlots,
    int EffectiveSlots,
    string? ThrottleBucket,
    // Throttle stages per bucket, drawn (and dragged) on the usage-monitor gauges. Defaults match
    // the DB defaults so an older worker that doesn't send them yet still yields sane markers.
    int ThrottleFiveHourSoftPct = 50,
    int ThrottleFiveHourHardPct = 65,
    int ThrottleSevenDaySoftPct = 50,
    int ThrottleSevenDayHardPct = 65);

public record ModelUsageRowDto(
    DateOnly Date,
    string Model,
    string Scope,
    long InputTokens,
    long OutputTokens,
    long CacheReadTokens,
    long CacheCreationTokens,
    int Messages,
    double? CostUsd = null);

public record TaskUsageRowDto(
    string TaskId,
    string TaskTitle,
    string ListId,
    string ListName,
    string? Model,
    int Runs,
    long TokensIn,
    long TokensOut,
    double? CostUsd = null,
    int? Retries = null,
    bool? Productive = null,
    bool? OneShot = null);

public record TokenTrackerDashboardDto(bool Ok, string? Url, string? Error);

public record TokenTrackerStatusDto(
    bool Installed,
    string? Version,
    bool NodeOk,
    string? NodeVersion,
    DateTime? LastFetchedUtc,
    string? LastError,
    int? FormatVersion,
    int SessionCount);

// What an embedded ConPTY terminal (UI process) needs to start a real `claude` process for
// a task's worktree, with the same setup as an autonomous run (session-skills seeded onto
// disk, the same run environment variables) plus the --resume-vs-fresh-start choice.
public sealed record LaunchSpec(
    string Cwd,
    string Exe,
    IReadOnlyList<string> Args,
    IReadOnlyDictionary<string, string> Env);

public record TicketSettingsDto(string? ApiBaseUrl, bool TokenSet);
public record TicketConnectionDto(bool Ok, string? UserName, IReadOnlyList<string> Scopes, string? Error);
public record TicketProjectDto(int Id, string Title, string DepartmentName);
public record TicketImportResultDto(int Examined, int Created);
