import Foundation

final class PostListPageGenerator {

    let source: PostListPageGeneratorSource

    init(source: PostListPageGeneratorSource) {
        self.source = source
    }

    private var language: ContentLanguage {
        source.language
    }

    private var mainContentMaximumWidth: Int {
        source.content.settings.posts.contentWidth
    }

    private func pageUrl(in language: ContentLanguage, pageNumber: Int) -> String {
        source.content.settings.general.url + pageUrlWithoutDomain(in: language, pageNumber: pageNumber)
    }

    private func pageUrlWithoutDomain(in language: ContentLanguage, pageNumber: Int) -> String {
        let base = source.pageUrlPrefix(for: language)
        guard pageNumber > 1 else {
            return base
        }
        return base + "/\(pageNumber)"
    }

    private func filePath(in language: ContentLanguage, pageNumber: Int) -> String {
        "\(source.pageUrlPrefix(for: language))/\(pageNumber).html"
    }

    /**
     Create pages for the given posts, sorted by date (newest first)
     */
    func createPages(for posts: [Post]) {
        // Sort by newest first, filter drafts
        let posts = posts
            .filter { !$0.isDraft }
            .sortedByStartDateAndId()

        let totalCount = posts.count
        guard totalCount > 0 else {
            // Create one empty page
            createPostFeedPage(1, pageCount: 1, posts: [])
            return
        }
        let postsPerPage = source.postsPerPage

        let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
        for pageIndex in 1...numberOfPages {
            let startIndex = (pageIndex - 1) * postsPerPage
            let endIndex = min(pageIndex * postsPerPage, totalCount)
            let postsOnPage = posts[startIndex..<endIndex]
            createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage)
        }
    }

    private func makePostData(post: Post, results: PageGenerationResults) -> FeedEntryData {
        let localized: LocalizedPost = post.localized(in: language)

        let linkUrl: FeedEntryData.Link? = post.linkedPage.map {
            guard !$0.isDraft else { return nil }
            return .init(
                url: $0.absoluteUrl(in: language),
                text: localized.pageLinkText ?? post.content.settings.posts.localized(in: language).defaultPageLinkText)
        }

        // Use the tags of the page if one is linked
        let tags: [FeedEntryData.Tag] = post.usedTags().filter { $0.isVisible }.map { tag in
                .init(name: tag.localized(in: language).name,
                      url: tag.absoluteUrl(in: language))
        }

        let media: FeedEntryData.Media?
        if localized.hasImages {
            let images = localized.images.map { image in
                image.imageSet(width: mainContentMaximumWidth, height: mainContentMaximumWidth, language: language)
            }
            images.forEach(source.results.require)
            media = .images(images)
        } else if localized.hasVideos {
            let videos: [PostVideo.Video] = localized.images.compactMap { file -> PostVideo.Video? in
                guard file.type.isVideo else {
                    self.source.results.warning("File \(file.identifier) ignored due to videos present in the post")
                    return nil
                }
                guard let type = file.videoType() else {
                    self.source.results.warning("Video \(file.identifier) ignored due to unknown video type")
                    return nil
                }
                return .init(path: file.absoluteUrl, type: type)
            }
            media = .video(videos)
            localized.images.forEach(source.results.require)
        } else {
            media = nil
        }

        let text = PostContentGenerator(
            content: source.content,
            results: source.results,
            language: language,
            post: post).generate()
        
        return FeedEntryData(
            entryId: post.identifier,
            title: localized.title,
            textAboveTitle: post.dateText(in: language),
            link: linkUrl,
            tags: tags,
            labels: localized.labels,
            text: text,
            media: media)
    }

    private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>) {
        let posts: [FeedEntryData] = posts.map { makePostData(post: $0, results: source.results) }

        let feedPageGenerator = FeedPageGenerator(content: source.content, results: source.results)

        // Includes leading slash
        let languageButtonUrl = pageUrlWithoutDomain(in: language.next, pageNumber: pageIndex)

        // Includes leading slash
        let pageUrl = pageUrl(in: language, pageNumber: pageIndex)

        #warning("Use results for each individual posts page")

        let fileContent = feedPageGenerator.generatePage(
            language: language,
            posts: posts,
            title: source.linkTitle,
            description: source.linkDescription,
            image: source.linkImage,
            pageUrl: pageUrl,
            pageTitle: source.pageTitle,
            pageNumber: pageIndex,
            totalPages: pageCount,
            languageButtonUrl: languageButtonUrl,
            linkPrefix: source.pageUrlPrefix(for: language))

        let filePath = self.filePath(in: language, pageNumber: pageIndex)
        guard save(fileContent, to: filePath) else {
            source.results.unsavedOutput(filePath, source: .feed)
            return
        }
    }

    private func save(_ content: String, to relativePath: String) -> Bool {
        source.content.storage.write(content, to: relativePath)
    }
}
