import Foundation

// Mastodon-compat Codable mirrors of the subset of Status / Account /
// MediaAttachment the watch reads. Phone-side type definitions live in
// src/lib/api-types.d.ts — keep this file in sync with the fields the
// watch actually consumes (id, media_attachments[].url, etc.).
//
// All optional fields are tolerant — Pixelfed and Mastodon servers
// occasionally omit fields the spec marks required, and the watch
// should keep rendering rather than crash.

struct WatchAccount: Codable, Hashable, Identifiable {
    let id: String
    let username: String
    let acct: String
    let displayName: String?
    let avatar: String?
    let url: String?

    enum CodingKeys: String, CodingKey {
        case id, username, acct, avatar, url
        case displayName = "display_name"
    }
}

struct WatchMediaAttachment: Codable, Hashable, Identifiable {
    let id: String
    let type: String
    let url: String?
    let previewUrl: String?
    let description: String?
    let blurhash: String?

    enum CodingKeys: String, CodingKey {
        case id, type, url, description, blurhash
        case previewUrl = "preview_url"
    }
}

struct WatchStatus: Codable, Hashable, Identifiable {
    let id: String
    let content: String?
    let createdAt: String?
    let account: WatchAccount
    let mediaAttachments: [WatchMediaAttachment]
    let favourited: Bool?
    let favouritesCount: Int?
    let sensitive: Bool?
    let spoilerText: String?

    enum CodingKeys: String, CodingKey {
        case id, content, account, favourited, sensitive
        case createdAt = "created_at"
        case mediaAttachments = "media_attachments"
        case favouritesCount = "favourites_count"
        case spoilerText = "spoiler_text"
    }

    /// Best-effort thumbnail for any media-bearing post.
    ///
    /// Pixelfed/Mastodon `MediaAttachment.type` is one of
    /// `unknown | image | gifv | video | audio`. Pixelfed's per-post
    /// `pf_type` discriminator (photo / video / text / reply / event /
    /// article / quote / album variants) lives on the Status, not the
    /// attachment — `WatchStatus` doesn't model it because the watch's
    /// only render path is "show a thumbnail" and the attachment-level
    /// `type` is sufficient.
    ///
    /// Resolution rules, in order:
    ///   - `image`     → `preview_url` (640×640 thumbnail, ≤~150 KB);
    ///                   fall back to `url` (full original, multi-MB)
    ///                   only when the server didn't generate a thumb.
    ///                   Originals routinely exceed the 12s request
    ///                   timeout on Watch radio bandwidth.
    ///   - `gifv`/`video` → `preview_url` (still poster); fall back to
    ///                      `url` only as a last resort (the watch can't
    ///                      decode video frames, but a server may put a
    ///                      poster JPEG at the same path)
    ///   - `audio`/`unknown` → skip (no useful still)
    ///
    /// Returns `nil` only for posts that are genuinely text-only or
    /// audio-only — those get filtered out of the rendered feed list.
    var thumbnailUrl: URL? {
        for m in mediaAttachments {
            switch m.type {
            case "image":
                if let s = m.previewUrl ?? m.url, let u = URL(string: s) {
                    return u
                }
            case "gifv", "video":
                if let s = m.previewUrl ?? m.url, let u = URL(string: s) {
                    return u
                }
            default:
                continue
            }
        }
        return nil
    }
}

struct TapestryBucketResponse: Codable, Hashable {
    let bucket: String
    let letters: String
}

// Capability advertisement — `metadata.config.features.halflight.tapestry`
// (or `metadata.halflight.features.tapestry`) is the gate the watch
// checks before mounting the tapestry view. See src/lib/features.ts.
struct NodeinfoResponse: Codable {
    let metadata: NodeinfoMetadata?
}

struct NodeinfoMetadata: Codable {
    let halflight: HalflightMetaShape?
    let config: NodeinfoConfigShape?
}

struct NodeinfoConfigShape: Codable {
    let features: NodeinfoConfigFeaturesShape?
}

struct NodeinfoConfigFeaturesShape: Codable {
    let halflight: HalflightFeaturesShape?
}

struct HalflightMetaShape: Codable {
    let features: HalflightFeaturesShape?
}

struct HalflightFeaturesShape: Codable {
    let tapestry: Bool?
    let profile: Bool?
}
