import Foundation
import SwiftUI
import Combine

final class Content: ObservableObject {

    @Published
    var didLoadContent = false

    @ObservedObject
    var storage: Storage

    @Published
    var settings: Settings

    @Published
    var posts: [Post]

    @Published
    var pages: [Page]

    @Published
    var tags: [Tag]

    @Published
    var files: [FileResource]

    @Published
    var tagOverview: Tag?

    @Published
    var results: GenerationResults

    @Published
    var generationStatus: String = "Ready to generate"

    @Published
    private(set) var isGeneratingWebsite = false

    @Published
    private(set) var shouldGenerateWebsite = false

    let imageGenerator: ImageGenerator

    init(settings: Settings,
         posts: [Post],
         pages: [Page],
         tags: [Tag],
         files: [FileResource],
         tagOverview: Tag?) {
        self.settings = settings
        self.posts = posts
        self.pages = pages
        self.tags = tags
        self.files = files
        self.tagOverview = tagOverview
        self.results = .init()

        let storage = Storage()
        self.storage = storage
        self.imageGenerator = ImageGenerator(
            storage: storage,
            settings: settings)
    }

    init() {
        let settings = Settings.default
        self.settings = settings
        self.posts = []
        self.pages = []
        self.tags = []
        self.files = []
        self.tagOverview = nil
        self.results = .init()

        let storage = Storage()
        self.storage = storage
        self.imageGenerator = ImageGenerator(
            storage: storage,
            settings: settings)
    }

    private func clear() {
        self.settings = .default
        self.posts = []
        self.pages = []
        self.tags = []
        self.files = []
        self.tagOverview = nil
        self.results = .init()
    }

    var images: [FileResource] {
        files.filter { $0.type.isImage }
    }

    func set(isGenerating: Bool) {
        self.isGeneratingWebsite = isGenerating
    }

    func set(shouldGenerate: Bool) {
        self.shouldGenerateWebsite = shouldGenerate
    }

    func add(_ file: FileResource) {
        // TODO: Insert at correct index?
        files.insert(file, at: 0)
    }

    func add(_ page: Page) {
        // TODO: Insert at correct index?
        pages.insert(page, at: 0)
    }

    func update(contentPath: URL, callback: @escaping ([String]) -> ()) {
        guard storage.save(contentPath: contentPath) else {
            return
        }
        loadFromDisk(callback: callback)
    }

    func remove(_ file: FileResource) {
        files.remove(file)
        for post in posts {
            post.remove(file)
        }

        for page in pages {
            page.remove(file)
        }

        for tag in tags {
            tag.remove(file)
        }
        settings.remove(file)
    }

    func file(withOutputPath: String) -> FileResource? {
        files.first { $0.absoluteUrl == withOutputPath }
    }

    private let errorPrinter = ErrorPrinter()

    func loadFromDisk(callback: @escaping (_ errors: [String]) -> ()) {
        defer {
            storage.contentScope?.delegate = errorPrinter
        }
        DispatchQueue.global().async {
            let loader = ModelLoader(content: self, storage: self.storage)
            let result = loader.load()
            guard result.errors.isEmpty else {
                DispatchQueue.main.async {
                    self.didLoadContent = false
                    callback(result.errors.sorted())
                }
                return
            }

            DispatchQueue.main.async {
                self.files = result.files
                self.posts = result.posts
                self.pages = result.pages
                self.tags = result.tags
                self.settings = result.settings
                self.tagOverview = result.tagOverview
                self.didLoadContent = true
                callback([])
                self.generateMissingVideoThumbnails()
            }
        }
    }

    func generateMissingVideoThumbnails() {
        Task {
            for file in self.files {
                guard file.type.isVideo else { continue }
                guard !file.isExternallyStored else { continue }
                guard !storage.hasVideoThumbnail(for: file.id) else { continue }
                if await imageGenerator.createVideoThumbnail(for: file.id) {
                    print("Generated thumbnail for \(file.id)")
                    file.didChange()
                }
            }
        }
    }
}
