import Foundation

// MARK: - Entry

struct ReaderFeedRef: Codable, Hashable {
    let id: Int
    let title: String
}

struct ReaderEntry: Codable, Identifiable, Hashable {
    let id: Int
    let title: String?
    let url: String?
    let content: String?
    let fullContent: String?
    let author: String?
    let publishedAt: String?
    var status: String       // mutable for local-first read/unread updates
    var starred: Bool        // mutable for local-first star updates
    let readingTime: Int
    let thumbnail: String?
    let feed: ReaderFeedRef?

    var isRead: Bool { status == "read" }

    var displayTitle: String {
        title ?? "(Untitled)"
    }

    var feedName: String {
        feed?.title ?? ""
    }

    var timeAgo: String {
        guard let publishedAt, let date = Self.parseDate(publishedAt) else { return "" }
        let interval = Date().timeIntervalSince(date)
        if interval < 60 { return "now" }
        if interval < 3600 { return "\(Int(interval / 60))m" }
        if interval < 86400 { return "\(Int(interval / 3600))h" }
        if interval < 604800 { return "\(Int(interval / 86400))d" }
        return "\(Int(interval / 604800))w"
    }

    var readingTimeText: String {
        "\(readingTime) min"
    }

    var articleHTML: String {
        fullContent ?? content ?? ""
    }

    var thumbnailURL: URL? {
        // Prefer server-provided thumbnail
        if let thumb = thumbnail, !thumb.isEmpty, let url = URL(string: thumb) {
            return url
        }
        // Fallback: extract from content
        return ReaderEntry.extractThumbnail(from: content ?? fullContent ?? "")
    }

    private static let imgRegex = try! NSRegularExpression(
        pattern: #"<img[^>]+src=["']([^"']+)["']"#,
        options: .caseInsensitive
    )

    static func extractThumbnail(from html: String) -> URL? {
        guard !html.isEmpty else { return nil }
        // Only search first 2000 chars for performance
        let searchRange = html.prefix(2000)
        let nsRange = NSRange(searchRange.startIndex..., in: searchRange)
        guard let match = imgRegex.firstMatch(in: String(searchRange), range: nsRange),
              match.numberOfRanges > 1,
              let srcRange = Range(match.range(at: 1), in: searchRange) else {
            return nil
        }
        let src = String(searchRange[srcRange])
            .replacingOccurrences(of: "&amp;", with: "&")
            .replacingOccurrences(of: "&lt;", with: "<")
            .replacingOccurrences(of: "&gt;", with: ">")
        return URL(string: src)
    }

    private static let isoFormatter: ISO8601DateFormatter = {
        let f = ISO8601DateFormatter()
        f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        return f
    }()

    private static let isoFormatterNoFrac: ISO8601DateFormatter = {
        let f = ISO8601DateFormatter()
        f.formatOptions = [.withInternetDateTime]
        return f
    }()

    static func parseDate(_ str: String) -> Date? {
        isoFormatter.date(from: str) ?? isoFormatterNoFrac.date(from: str)
    }
}

struct ReaderEntryList: Codable {
    let total: Int
    let entries: [ReaderEntry]
}

// MARK: - Feed

struct ReaderCategoryRef: Codable, Hashable {
    let id: Int
    let title: String
}

struct ReaderFeed: Codable, Identifiable, Hashable {
    let id: Int
    let title: String
    let feedUrl: String
    let siteUrl: String?
    let category: ReaderCategoryRef?
}

// MARK: - Category

struct ReaderCategory: Codable, Identifiable, Hashable {
    let id: Int
    let title: String
}

// MARK: - Counters

struct ReaderCounters: Codable {
    let unreads: [String: Int]

    func count(forFeed feedId: Int) -> Int {
        unreads[String(feedId)] ?? 0
    }

    var totalUnread: Int {
        unreads.values.reduce(0, +)
    }
}

// MARK: - Request Bodies

struct ReaderBulkUpdate: Codable {
    let entryIds: [Int]
    let status: String
}

struct ReaderMarkAllRead: Codable {
    let feedId: Int?
    let categoryId: Int?
}

struct ReaderFeedCreate: Codable {
    let feedUrl: String
    let categoryId: Int?
}

struct ReaderCategoryCreate: Codable {
    let title: String
}

// MARK: - Responses

struct ReaderBookmarkResponse: Codable {
    let starred: Bool
}

struct ReaderRefreshResponse: Codable {
    let ok: Bool
    let message: String?
}

struct ReaderMarkAllReadResponse: Codable {
    let ok: Bool
    let marked: Int?
}
