import Foundation

struct BookRemote: Codable, Identifiable, Hashable {
    var id: String { isbn13 ?? isbn10 ?? title + (authors.first ?? "") }

    let remoteId: String?
    let title: String
    let authors: [String]
    let publishedYear: Int?
    let isbn10: String?
    let isbn13: String?
    let coverImageUrl: URL?
    let language: String?
    let description: String?
    let pageCount: Int?
    let categories: [String]
    let publisher: String?
    let sourceLocale: String?
    let readingStatus: ReadingStatus?
    let readingProgress: Double?

    init(
        remoteId: String? = nil,
        title: String,
        authors: [String] = [],
        publishedYear: Int? = nil,
        isbn10: String? = nil,
        isbn13: String? = nil,
        coverImageUrl: URL? = nil,
        language: String? = nil,
        description: String? = nil,
        pageCount: Int? = nil,
        categories: [String] = [],
        publisher: String? = nil,
        sourceLocale: String? = nil,
        readingStatus: ReadingStatus? = nil,
        readingProgress: Double? = nil
    ) {
        self.remoteId = remoteId
        self.title = title
        self.authors = authors
        self.publishedYear = publishedYear
        self.isbn10 = isbn10
        self.isbn13 = isbn13
        self.coverImageUrl = coverImageUrl
        self.language = language
        self.description = description
        self.pageCount = pageCount
        self.categories = categories
        self.publisher = publisher
        self.sourceLocale = sourceLocale
        self.readingStatus = readingStatus
        self.readingProgress = readingProgress
    }
}

struct BookSearchResponse: Decodable {
    let items: [BookRemote]

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        // Endpoint 1: /isbn returns { data: { tr: {...}, en: {...} } }
        if let localeMap = try? container.decode([String: RawBook].self, forKey: .data) {
            self.items = localeMap.map { locale, raw in raw.toBook(locale: locale) }
            return
        }

        // Endpoint 2/3: /title and /filter returns { data: [{ locale, items: [...] }] }
        if let groups = try? container.decode([LocaleGroup].self, forKey: .data) {
            self.items = groups.flatMap { group in
                group.items.map { $0.toBook(locale: group.locale) }
            }
            return
        }

        self.items = []
    }

    private enum CodingKeys: String, CodingKey {
        case data
    }
}

private struct LocaleGroup: Decodable {
    let locale: String
    let items: [RawBook]
}

private struct RawBook: Decodable {
    let asin: String?
    let title: String?
    let authorName: String?
    let author: String?
    let isbn: String?
    let thumbImage: String?
    let image: String?
    let date: String?
    let publisher: String?
    let page: Int?
    let description: String?
    let categories: [String]?
    let locale: String?

    func toBook(locale: String?) -> BookRemote {
        let coverRaw = thumbImage ?? image
        let authorsText = authorName ?? author ?? ""
        let authors = authorsText
            .replacingOccurrences(of: "[Yazar]", with: "")
            .split(separator: ",")
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty }

        let isbnClean = isbn?.filter { $0.isNumber || $0.uppercased() == "X" }
        let isbn10 = isbnClean?.count == 10 ? isbnClean : nil
        let isbn13 = isbnClean?.count == 13 ? isbnClean : nil

        return BookRemote(
            remoteId: asin,
            title: title ?? "Untitled",
            authors: authors,
            publishedYear: Self.extractYear(from: date),
            isbn10: isbn10,
            isbn13: isbn13,
            coverImageUrl: coverRaw.flatMap(URL.init(string:)),
            language: locale,
            description: description,
            pageCount: page,
            categories: categories ?? [],
            publisher: publisher,
            sourceLocale: locale
        )
    }

    private static func extractYear(from value: String?) -> Int? {
        guard let value else { return nil }
        let digits = value.components(separatedBy: CharacterSet.decimalDigits.inverted).joined()
        guard digits.count >= 4 else { return nil }
        return Int(String(digits.prefix(4)))
    }
}
