import Foundation

extension Content {

    private func convert(_ tag: LocalizedTagFile, images: [String : FileResource]) -> LocalizedTag {
        LocalizedTag(
            content: self,
            urlComponent: tag.urlComponent,
            name: tag.name,
            linkPreviewTitle: tag.linkPreviewTitle,
            linkPreviewDescription: tag.linkPreviewDescription,
            linkPreviewImage: tag.linkPreviewImage.map { images[$0] },
            originalUrl: tag.originalURL)
    }

    private func convert(_ post: LocalizedPostFile, images: [String : FileResource]) -> LocalizedPost {
        LocalizedPost(
            content: self,
            title: post.title,
            text: post.content,
            lastModified: post.lastModifiedDate,
            images: post.images.compactMap { images[$0] },
            linkPreviewImage: post.linkPreviewImage.map { images[$0] },
            linkPreviewTitle: post.linkPreviewTitle,
            linkPreviewDescription: post.linkPreviewDescription)
    }

    private func convert(_ page: LocalizedPageFile, images: [String : FileResource]) -> LocalizedPage {
        LocalizedPage(
            content: self,
            urlString: page.url,
            title: page.title,
            lastModified: page.lastModifiedDate,
            originalUrl: page.originalURL,
            linkPreviewImage: page.linkPreviewImage.map { images[$0] },
            linkPreviewTitle: page.linkPreviewTitle,
            linkPreviewDescription: page.linkPreviewDescription,
            hideTitle: page.hideTitle ?? false)
    }

    func loadFromDisk() throws {
        guard storage.contentScope != nil else {
            print("Storage not initialized, not loading content")
            throw StorageAccessError.noBookmarkData
        }

        let settings = storage.loadSettings() ?? .default
        let imageDescriptions = storage.loadFileDescriptions()
            .default([])
            .reduce(into: [:]) { $0[$1.fileId] = $1 }

        guard let tagData = storage.loadAllTags() else {
            print("Failed to load file tags")
            return
        }
        if tagData.isEmpty { print("No tags loaded") }

        guard let pagesData = storage.loadAllPages() else {
            print("Failed to load file pages")
            return
        }
        if pagesData.isEmpty { print("No pages loaded") }

        guard let postsData = storage.loadAllPosts() else {
            print("Failed to load file posts")
            return
        }
        if postsData.isEmpty { print("No posts loaded") }

        guard let fileList = storage.loadAllFiles() else {
            print("Failed to load file list")
            return
        }
        if fileList.isEmpty { print("No files loaded") }

        let externalFiles = storage.loadExternalFileList() ?? []
        if externalFiles.isEmpty { print("No external files loaded") }

        let tagOverviewData = storage.loadTagOverview()
        if tagOverviewData == nil { print("No tag overview loaded") }

        print("Loaded data from disk, processing...")
        // All data loaded from storage, start constructing the data model

        var files: [String : FileResource] = fileList.reduce(into: [:]) { files, fileId in
            let descriptions = imageDescriptions[fileId]
            files[fileId] = FileResource(
                content: self,
                id: fileId,
                isExternallyStored: false,
                en: descriptions?.english ?? "",
                de: descriptions?.german ?? "")
        }

        for fileId in externalFiles {
            let descriptions = imageDescriptions[fileId]
            files[fileId] = FileResource(
                content: self,
                id: fileId,
                isExternallyStored: true,
                en: descriptions?.english ?? "",
                de: descriptions?.german ?? "")
        }

        let images = files.filter { $0.value.type.isImage }

        let tags = tagData.reduce(into: [:]) { (tags, data) in
            tags[data.key] = Tag(
                content: self,
                id: data.value.id,
                isVisible: data.value.isVisible,
                german: convert(data.value.german, images: images),
                english: convert(data.value.english, images: images))
        }

        let pages: [String : Page] = loadPages(pagesData, tags: tags, files: files)

        let posts: [String : Post] = postsData.reduce(into: [:]) { dict, data in
            let (postId, post) = data
            let linkedPage = post.linkedPageId.map { pages[$0] }
            let german = convert(post.german, images: images)
            let english = convert(post.english, images: images)

            dict[postId] = Post(
                content: self,
                id: postId,
                isDraft: post.isDraft,
                createdDate: post.createdDate,
                startDate: post.startDate,
                endDate: post.endDate,
                tags: post.tags.map { tags[$0]! },
                german: german,
                english: english,
                linkedPage: linkedPage)
        }

        let tagOverview = tagOverviewData.map { file in
            TagOverviewPage(
                content: self,
                german: .init(content: self, file: file.german, image: file.german.linkPreviewImage.map { files[$0] }),
                english: .init(content: self, file: file.english, image: file.english.linkPreviewImage.map { files[$0] }))
        }

        self.tags = tags.values.sorted()
        self.pages = pages.values.sorted(ascending: false) { $0.startDate }
        self.files = files.values.sorted { $0.id }
        self.posts = posts.values.sorted(ascending: false) { $0.startDate }
        self.tagOverview = tagOverview
        self.settings = .init(file: settings, files: files) { raw in
            #warning("Notify about missing links")
            guard let type = ItemType(rawValue: raw, posts: posts, pages: pages, tags: tags) else {
                return nil
            }
            switch type {
            case .general:
                return nil
            case .post(let post):
                return post
            case .feed:
                return nil // TODO: Provide feed object
            case .page(let page):
                return page
            case .tagPage(let tag):
                return tag
            case .tagOverview:
                return tagOverview
            }
        }

        print("Content loaded")
    }

    private func loadPages(_ pagesData: [String : PageFile], tags: [String : Tag], files: [String : FileResource]) -> [String : Page] {
        pagesData.reduce(into: [:]) { pages, data in
            let (pageId, page) = data
            pages[pageId] = Page(
                content: self,
                id: pageId,
                externalLink: page.externalLink,
                isDraft: page.isDraft,
                createdDate: page.createdDate,
                hideDate: page.hideDate ?? false,
                startDate: page.startDate,
                endDate: page.endDate,
                german: convert(page.german, images: files),
                english: convert(page.english, images: files),
                tags: page.tags.compactMap { tags[$0] },
                requiredFiles: page.requiredFiles?.compactMap { files[$0] } ?? [])
        }
    }

}
