
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.linkPreview.title ?? 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 image: String?

    let iconUrl: String

    let links: [NavigationBar.Link]

    let headers: Set<HeaderElement>

    let basePath: String

    let websiteUrl: String

    let localizedBaseUrl: String

    /**
     The path (relative to the output folder) for the html file
     */
    func filePath(pageNumber: Int) -> String {
        basePath + "/\(pageNumber).html"
    }

    /**
     The absolute url (without domain) for the language button of the page
     */
    private func languageButtonUrl(page: Int) -> String {
        guard page > 1 else {
            return localizedBaseUrl
        }
        return localizedBaseUrl + "/\(page)"
    }

    /**
     The full url (including domain) to the page.
     */
    private func pageUrl(page: Int) -> String {
        guard page > 1 else {
            return websiteUrl + basePath
        }
        return websiteUrl + basePath + "/\(page)"
    }

    func pageHeader(pageNumber: Int) -> PageHeader {
        .init(
            language: language,
            title: title,
            description: description,
            image: image,
            pageUrl: pageUrl(page: pageNumber),
            iconUrl: iconUrl,
            languageButton: .init(
                text: language.next.rawValue,
                url: languageButtonUrl(page: 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: Tag) {
        let localized = overview.localized(in: language)

        let imageUrl = localized.linkPreview.image?.linkPreviewImage(results: results)

        let header = TagHeaderContent(
            language: language,
            title: localized.linkPreview.title ?? localized.title,
            description: localized.linkPreview.description,
            image: imageUrl,
            iconUrl: content.settings.navigation.localized(in: language).rootUrl,
            links: content.navigationBar(in: language),
            headers: content.postPageHeaders,
            basePath: overview.absoluteUrl(in: language),
            websiteUrl: content.settings.general.url,
            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.linkPreview.description ?? ""
                    let image = tag.localized.linkPreview.image.map {
                        let size = self.content.settings.pages.pageLinkImageSize
                        let imageSet = $0.imageSet(width: size, height: size, language: self.language)
                        self.results.require(imageSet: imageSet)
                        return imageSet
                    }

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

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