import APIClient
import Foundation
import Observation
import WireProtocol

/// T-iOS-26 · 项目详情状态（`GET /projects/detail?path=` → phase 状态机，
/// 与 DiffViewModel/TimelineViewModel 同一纪律）。
///
/// fetch 闭包注入 —— 生产由 `forHost` 包 `APIClient.projectDetail(path:)`
/// （builder 百分号编码、400/404/500 `{error}` → 类型化错误均在 T-iOS-38
/// 完成并已测）；测试注入 fake。本 VM 只归约用户可见的三种结局：
/// - 成功 → `.loaded(ProjectDetail)`（sessions/worktrees/hasClaudeMd 透传）；
/// - 400 → `.failed(.pathInvalid)`、404 → `.failed(.notFound)`、
///   500/解码/传输 → `.failed(.unavailable)` —— 全部可经 `load()` 重试。
@MainActor
@Observable
final class ProjectDetailViewModel {
    /// 用户可见的失败三分类（文案映射在 ProjectDetailScreen）。
    enum Failure: Equatable {
        case pathInvalid
        case notFound
        case unavailable
    }

    enum Phase: Equatable {
        case loading
        case loaded(ProjectDetail)
        case failed(Failure)
    }

    private(set) var phase: Phase = .loading
    /// 详情/diff 的目标项目路径（来自列表行 —— 服务器数据；只透传给
    /// builder，绝不本地拼 URL）。
    let path: String

    @ObservationIgnored
    private let fetch: @Sendable () async throws -> ProjectDetail

    init(path: String, fetch: @escaping @Sendable () async throws -> ProjectDetail) {
        self.path = path
        self.fetch = fetch
    }

    /// 生产装配缝（ProjectsViewModel.makeDetailViewModel 经此构造）。
    static func forHost(
        endpoint: HostEndpoint, http: any HTTPTransport, path: String
    ) -> ProjectDetailViewModel {
        let client = APIClient(endpoint: endpoint, http: http)
        return ProjectDetailViewModel(path: path, fetch: {
            try await client.projectDetail(path: path)
        })
    }

    /// Fetch 并呈现。也是「重试」路径：`.failed` 后可再次调用。
    func load() async {
        phase = .loading
        do {
            phase = .loaded(try await fetch())
        } catch let error as APIClientError {
            phase = .failed(Self.failure(for: error))
        } catch {
            phase = .failed(.unavailable) // 传输层等其余错误：可重试兜底
        }
    }

    private static func failure(for error: APIClientError) -> Failure {
        switch error {
        case .projectPathInvalid, .invalidRequest:
            return .pathInvalid
        case .projectNotFound:
            return .notFound
        default:
            return .unavailable
        }
    }
}
