import APIClient
import SwiftUI
import WireProtocol

/// T-iOS-26 · 项目详情屏：sessions/worktrees/CLAUDE.md 渲染 + diff 入口
/// （T-iOS-27 的 `DiffScreen(endpoint:path:http:)`）+ "在此仓库开新会话"。
///
/// 安全：名字/路径/分支/CLAUDE.md 内容全是**不可信服务器字节** ——
/// 一律 `Text(verbatim:)`（绝不 LocalizedStringKey/Markdown/链接探测）。
struct ProjectDetailScreen: View {
    @State private var viewModel: ProjectDetailViewModel
    private let endpoint: HostEndpoint
    private let http: any HTTPTransport
    private let onOpenClaude: (String) -> Void
    @State private var isDiffPresented = false

    private enum Metrics {
        /// CLAUDE.md 预览行数上限（内容裁剪，非视觉 token）。
        static let claudeMdLineLimit = 40
    }

    init(
        viewModel: ProjectDetailViewModel,
        endpoint: HostEndpoint,
        http: any HTTPTransport,
        onOpenClaude: @escaping (String) -> Void
    ) {
        _viewModel = State(initialValue: viewModel)
        self.endpoint = endpoint
        self.http = http
        self.onOpenClaude = onOpenClaude
    }

    var body: some View {
        content
            .navigationTitle(ProjectDetailCopy.title)
            .navigationBarTitleDisplayMode(.inline)
            .task { await viewModel.load() }
            .navigationDestination(isPresented: $isDiffPresented) {
                DiffScreen(endpoint: endpoint, path: viewModel.path, http: http)
            }
    }

    // MARK: - Phase switch

    @ViewBuilder private var content: some View {
        switch viewModel.phase {
        case .loading:
            ProgressView()
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        case .failed(let failure):
            failedState(failure)
        case .loaded(let detail):
            detailList(detail)
        }
    }

    /// 显式、可重试的错误态（400/404/500 `{error}` → 分类文案，任务 Steps）。
    private func failedState(_ failure: ProjectDetailViewModel.Failure) -> some View {
        let copy = Self.failureCopy(failure)
        return ContentUnavailableView {
            Label(copy.title, systemImage: "exclamationmark.triangle")
        } description: {
            Text(copy.detail)
        } actions: {
            Button(ProjectDetailCopy.retry) {
                Task { await viewModel.load() }
            }
            .buttonStyle(.borderedProminent)
            .tint(DS.Palette.accent)
        }
    }

    static func failureCopy(
        _ failure: ProjectDetailViewModel.Failure
    ) -> (title: String, detail: String) {
        switch failure {
        case .pathInvalid:
            return (ProjectDetailCopy.failedPathInvalid,
                    ProjectDetailCopy.failedPathInvalidDetail)
        case .notFound:
            return (ProjectDetailCopy.failedNotFound,
                    ProjectDetailCopy.failedNotFoundDetail)
        case .unavailable:
            return (ProjectDetailCopy.failedUnavailable,
                    ProjectDetailCopy.failedUnavailableDetail)
        }
    }

    // MARK: - Loaded

    private func detailList(_ detail: ProjectDetail) -> some View {
        List {
            headerSection(detail)
            actionsSection(detail)
            sessionsSection(detail.sessions)
            worktreesSection(detail.worktrees)
            claudeMdSection(detail)
        }
        .listStyle(.insetGrouped)
    }

    private func headerSection(_ detail: ProjectDetail) -> some View {
        Section {
            VStack(alignment: .leading, spacing: DS.Space.xs4) {
                Text(verbatim: detail.name)
                    .font(DS.Typography.headline)
                    .foregroundStyle(DS.Palette.textPrimary)
                    .lineLimit(1)
                // 路径是服务器字节 → verbatim + 等宽单行中段截断。
                Text(verbatim: detail.path)
                    .font(DS.Typography.mono(.caption))
                    .foregroundStyle(DS.Palette.textSecondary)
                    .lineLimit(1)
                    .truncationMode(.middle)
                HStack(spacing: DS.Space.sm8) {
                    if let branch = detail.branch {
                        Label {
                            Text(verbatim: branch).lineLimit(1)
                        } icon: {
                            Image(systemName: "arrow.triangle.branch")
                        }
                        .font(DS.Typography.caption)
                        .foregroundStyle(DS.Palette.textSecondary)
                    }
                    if detail.dirty == true {
                        DirtyBadge()
                    }
                }
            }
        }
    }

    private func actionsSection(_ detail: ProjectDetail) -> some View {
        Section {
            Button {
                DS.Haptics.selection()
                onOpenClaude(detail.path)
            } label: {
                Label(ProjectDetailCopy.openClaude, systemImage: "terminal.fill")
            }
            .buttonStyle(DSButtonStyle(kind: .primary))
            .listRowInsets(EdgeInsets(
                top: DS.Space.sm8, leading: DS.Space.lg16,
                bottom: detail.isGit ? DS.Space.xs4 : DS.Space.sm8, trailing: DS.Space.lg16
            ))
            .listRowBackground(Color.clear)
            if detail.isGit {
                Button {
                    isDiffPresented = true
                } label: {
                    Label(ProjectDetailCopy.viewDiff, systemImage: "plus.forwardslash.minus")
                }
                .buttonStyle(DSButtonStyle(kind: .secondary))
                .listRowInsets(EdgeInsets(
                    top: DS.Space.xs4, leading: DS.Space.lg16,
                    bottom: DS.Space.sm8, trailing: DS.Space.lg16
                ))
                .listRowBackground(Color.clear)
            }
        }
    }

    @ViewBuilder private func sessionsSection(_ sessions: [ProjectSessionRef]) -> some View {
        Section(ProjectDetailCopy.sessionsHeader) {
            if sessions.isEmpty {
                Text(ProjectDetailCopy.noSessions)
                    .font(DS.Typography.caption)
                    .foregroundStyle(DS.Palette.textSecondary)
            } else {
                ForEach(sessions, id: \.id) { session in
                    sessionRow(session)
                }
            }
        }
    }

    private func sessionRow(_ session: ProjectSessionRef) -> some View {
        HStack(spacing: DS.Space.sm8) {
            // 状态 = 色 + 形 + VoiceOver 标签（DS 单一状态真源）；退出优先。
            StatusBadge(status: session.exited ? .exited : DisplayStatus(session.status))
            // title 是派生的 cwd 尾段（服务器字节）→ verbatim；缺失退回短 id。
            Text(verbatim: session.title ?? String(
                session.id.uuidString.lowercased().prefix(8)
            ))
            .font(DS.Typography.body)
            .foregroundStyle(DS.Palette.textPrimary)
            .lineLimit(1)
            Spacer(minLength: 0)
            if session.exited {
                Text(ProjectDetailCopy.sessionExited)
                    .font(DS.Typography.caption)
                    .foregroundStyle(DS.Palette.textSecondary)
            } else {
                // 客户端数含数字 → 等宽 tabular 元信息。
                Text(ProjectDetailCopy.clientCount(session.clientCount))
                    .dsMetaText()
            }
        }
    }

    @ViewBuilder private func worktreesSection(_ worktrees: [WorktreeInfo]) -> some View {
        if !worktrees.isEmpty {
            Section(ProjectDetailCopy.worktreesHeader) {
                ForEach(worktrees, id: \.path) { worktree in
                    worktreeRow(worktree)
                }
            }
        }
    }

    private func worktreeRow(_ worktree: WorktreeInfo) -> some View {
        VStack(alignment: .leading, spacing: DS.Space.xs4) {
            HStack(spacing: DS.Space.sm8) {
                Text(verbatim: worktree.branch ?? ProjectDetailCopy.detachedHead)
                    .font(DS.Typography.callout)
                    .foregroundStyle(DS.Palette.textPrimary)
                    .lineLimit(1)
                if worktree.isMain {
                    TagBadge(text: ProjectDetailCopy.worktreeMain)
                }
                if worktree.isCurrent {
                    TagBadge(text: ProjectDetailCopy.worktreeCurrent)
                }
                if worktree.locked == true {
                    TagBadge(text: ProjectDetailCopy.worktreeLocked)
                }
            }
            Text(verbatim: worktree.path)
                .font(DS.Typography.mono(.caption))
                .foregroundStyle(DS.Palette.textSecondary)
                .lineLimit(1)
                .truncationMode(.middle)
        }
    }

    @ViewBuilder private func claudeMdSection(_ detail: ProjectDetail) -> some View {
        if detail.hasClaudeMd {
            Section(ProjectDetailCopy.claudeMdHeader) {
                if let content = detail.claudeMd {
                    // 服务器已截断供展示；仍是不可信字节 → verbatim + 行数上限。
                    Text(verbatim: content)
                        .font(DS.Typography.mono(.caption))
                        .foregroundStyle(DS.Palette.textPrimary)
                        .lineLimit(Metrics.claudeMdLineLimit)
                } else {
                    Text(ProjectDetailCopy.claudeMdPresent)
                        .font(DS.Typography.caption)
                        .foregroundStyle(DS.Palette.textSecondary)
                }
            }
        }
    }
}

// MARK: - 小徽标（DS token 化，跨 Projects/详情复用）

/// 「未提交」脏标：琥珀语义色文字 + 中性胶囊底（同 TelemetryChip 的 `.quaternary`）。
struct DirtyBadge: View {
    var body: some View {
        Text(ProjectsCopy.dirtyBadge)
            .font(DS.Typography.caption.weight(.medium))
            // 软填充胶囊：amber 字 + amber 淡底（非浅灰底），明暗两态都清晰。
            .foregroundStyle(DS.Palette.statusWaiting)
            .padding(.horizontal, DS.Space.sm8)
            .padding(.vertical, DS.Space.xs2)
            .background(DS.Palette.statusWaiting.opacity(0.18), in: Capsule())
    }
}

/// worktree 属性标签（主/当前/已锁定）：accent 描边胶囊。
struct TagBadge: View {
    let text: String

    var body: some View {
        Text(text)
            .font(DS.Typography.caption)
            .foregroundStyle(DS.Palette.accent)
            .padding(.horizontal, DS.Space.sm8)
            .padding(.vertical, DS.Space.xs2)
            .overlay(
                Capsule().strokeBorder(DS.Palette.accent, lineWidth: DS.Stroke.hairline)
            )
    }
}

// MARK: - 用户可见文案（中文具名常量，plan §4）

enum ProjectDetailCopy {
    static let title = "项目详情"
    static let openClaude = "在此仓库开新会话"
    static let viewDiff = "查看代码差异"
    static let sessionsHeader = "运行中的会话"
    static let noSessions = "暂无运行中的会话。"
    static let sessionExited = "已退出"
    static let worktreesHeader = "Worktrees"
    static let worktreeMain = "主"
    static let worktreeCurrent = "当前"
    static let worktreeLocked = "已锁定"
    static let detachedHead = "（分离 HEAD）"
    static let claudeMdHeader = "CLAUDE.md"
    static let claudeMdPresent = "本仓库包含 CLAUDE.md。"
    static let retry = "重试"
    static let failedPathInvalid = "路径无效"
    static let failedPathInvalidDetail = "请求的项目路径无效（服务器返回 400），请从项目列表重新进入。"
    static let failedNotFound = "项目未找到"
    static let failedNotFoundDetail = "该路径不在主机的项目根目录下，或已被移除（404）。"
    static let failedUnavailable = "详情加载失败"
    static let failedUnavailableDetail = "无法从主机获取项目详情，请检查连接后重试。"

    static func clientCount(_ count: Int) -> String {
        "\(count) 个客户端"
    }
}
