
private struct TagData {

    let url: String

    let title: String

    let localized: LocalizedTag

    init(tag: Tag, language: ContentLanguage) {
        let localized = tag.localized(in: language)
        self.url = tag.absoluteUrl(in: language)
        self.title = localized.linkPreviewTitle ?? localized.name
        self.localized = localized
    }
}

extension TagData: Comparable {

    static func < (lhs: TagData, rhs: TagData) -> Bool {
        lhs.title < rhs.title
    }

    static func == (lhs: TagData, rhs: TagData) -> Bool {
        lhs.title == rhs.title
    }
}

private struct TagHeaderContent {

    let language: ContentLanguage

    let title: String

    let description: String?

    let iconUrl: String

    let links: [NavigationBar.Link]

    let headers: Set<HeaderElement>

    let baseUrl: String

    let localizedBaseUrl: String

    private func url(pageNumber: Int) -> String {
        baseUrl + "/\(pageNumber)"
    }

    func fileUrl(pageNumber: Int) -> String {
        url(pageNumber: pageNumber) + ".html"
    }

    func pageHeader(pageNumber: Int) -> PageHeader {
        .init(
            language: language,
            title: title,
            description: description,
            iconUrl: iconUrl,
            languageButton: .init(
                text: language.next.rawValue,
                url: localizedBaseUrl + "/\(pageNumber)"),
            links: links,
            headers: headers,
            icons: [])
    }
}

final class TagOverviewGenerator {

    let content: Content

    let language: ContentLanguage
    
    let results: PageGenerationResults

    init(content: Content, language: ContentLanguage, results: PageGenerationResults) {
        self.content = content
        self.language = language
        self.results = results
    }

    func generatePages(tags: [Tag], overview: TagOverviewPage) {
        let localized = overview.localized(in: language)
        let header = TagHeaderContent(
            language: language,
            title: localized.linkPreviewTitle ?? localized.title,
            description: localized.linkPreviewDescription,
            iconUrl: content.settings.navigation.localized(in: language).rootUrl,
            links: content.navigationBar(in: language),
            headers: content.defaultPageHeaders,
            baseUrl: overview.absoluteUrl(in: language),
            localizedBaseUrl: overview.absoluteUrl(in: language.next))

        // Sort tags by title
        let tagData = tags.map {
            TagData(tag: $0, language: language)
        }.sorted { $0.title }

        let totalCount = tagData.count
        guard totalCount > 0 else {
            // Create one empty page
            generatePage(tags: [], header: header, page: 1, totalPages: 1)
            return
        }
        let tagsPerPage = content.settings.posts.postsPerPage

        let numberOfPages = (totalCount + tagsPerPage - 1) / tagsPerPage // Round up
        for pageIndex in 1...numberOfPages {
            let startIndex = (pageIndex - 1) * tagsPerPage
            let endIndex = min(pageIndex * tagsPerPage, totalCount)
            let tagsOnPage = tagData[startIndex..<endIndex]
            generatePage(tags: tagsOnPage, header: header, page: pageIndex, totalPages: numberOfPages)
        }
    }

    private func generatePage(tags: ArraySlice<TagData>, header: TagHeaderContent, page pageNumber: Int, totalPages: Int) {
        let pageHeader = header.pageHeader(pageNumber: pageNumber)

        let page = GenericPage(
            header: pageHeader,
            additionalFooter: "") { content in
                content += "<h1 class='separated-headline'>\(header.title)</h1>"
                for tag in tags {
                    let description = tag.localized.linkPreviewDescription ?? ""
                    let image = self.makePageImage(item: tag.localized)

                    content +=  RelatedPageLink(
                        title: tag.title,
                        description: description,
                        url: tag.url,
                        image: image)
                    .content
                }
                if totalPages > 1 {
                    content += PostFeedPageNavigation(
                        linkPrefix: header.baseUrl,
                        currentPage: pageNumber,
                        numberOfPages: totalPages).content
                }
            }
        let fileContent = page.content
        let url = header.fileUrl(pageNumber: pageNumber)

        guard content.storage.write(fileContent, to: url) else {
            results.unsavedOutput(url, source: .tagOverview)
            return
        }
    }

    private func makePageImage(item: LinkPreviewItem) -> ImageSet? {
        item.linkPreviewImage.map { image in
            let size = content.settings.pages.pageLinkImageSize
            let imageSet = image.imageSet(width: size, height: size, language: language)
            results.require(imageSet: imageSet)
            return imageSet
        }
    }
}
