package wang.yaojia.webterm.api.models

import kotlinx.serialization.Serializable
import wang.yaojia.webterm.wire.ClaudeStatus
import java.util.UUID

/**
 * One running session belonging to a project (`src/types.ts` `ProjectSessionRef`). The server
 * mirrors [LiveSessionInfo] fields into this ref. Tolerant: unknown status → [ClaudeStatus.UNKNOWN],
 * absent title → `null`; missing id/clientCount/createdAt/exited drops the ref (nested lossy list).
 */
@Serializable
public data class ProjectSessionRef(
    @Serializable(with = UuidSerializer::class) val id: UUID,
    val title: String? = null,
    @Serializable(with = ClaudeStatusSerializer::class) val status: ClaudeStatus = ClaudeStatus.UNKNOWN,
    val clientCount: Int,
    val createdAt: Long,
    val exited: Boolean,
)

/**
 * A discovered project (git repo or recently-used cwd) — `GET /projects` (`src/types.ts`
 * `ProjectInfo`). There is NO `namespace` field on the wire; namespace grouping is a client concept
 * surfaced only via `UiPrefs.collapsed` group-keys.
 */
@Serializable
public data class ProjectInfo(
    val name: String,
    val path: String,
    val isGit: Boolean,
    val branch: String? = null,
    /** Uncommitted changes; only present when the server runs the dirty check. */
    val dirty: Boolean? = null,
    /** Newest `~/.claude/projects` mtime for this cwd (ms) — the sort key. */
    val lastActiveMs: Long? = null,
    /** W3 sync chip — commits on HEAD not on `@{u}` (best-effort; absent when no upstream). */
    val ahead: Int? = null,
    /** W3 sync chip — commits on `@{u}` not on HEAD (best-effort; absent when no upstream). */
    val behind: Int? = null,
    /** HEAD commit time in ms (`git log -1 --format=%ct * 1000`); absent on a fresh/empty repo. */
    val lastCommitMs: Long? = null,
    @Serializable(with = ProjectSessionRefListSerializer::class)
    val sessions: List<ProjectSessionRef> = emptyList(),
)

/** One entry from `git worktree list --porcelain` (`src/types.ts` `WorktreeInfo`). */
@Serializable
public data class WorktreeInfo(
    val path: String,
    /** Branch name; `null` on detached HEAD. */
    val branch: String? = null,
    val head: String? = null,
    val isMain: Boolean = false,
    val isCurrent: Boolean = false,
    val locked: Boolean? = null,
    val prunable: Boolean? = null,
)

/** Detailed view of one project — `GET /projects/detail?path=` (`src/types.ts` `ProjectDetail`). */
@Serializable
public data class ProjectDetail(
    val name: String,
    val path: String,
    val isGit: Boolean,
    val branch: String? = null,
    val dirty: Boolean? = null,
    @Serializable(with = WorktreeInfoListSerializer::class)
    val worktrees: List<WorktreeInfo> = emptyList(),
    @Serializable(with = ProjectSessionRefListSerializer::class)
    val sessions: List<ProjectSessionRef> = emptyList(),
    val hasClaudeMd: Boolean = false,
    /** CLAUDE.md content (server-truncated for display) when present. */
    val claudeMd: String? = null,
)
