//
//  StatusMO.swift
//  Tusker
//
//  Created by Shadowfacts on 4/11/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//
//

import Foundation
import CoreData
import Pachyderm
import WebURLFoundationExtras

@objc(StatusMO)
public final class StatusMO: NSManagedObject, StatusProtocol {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<StatusMO> {
        return NSFetchRequest<StatusMO>(entityName: "Status")
    }
    
    @nonobjc public class func fetchRequest(id: String) -> NSFetchRequest<StatusMO> {
        let req = Self.fetchRequest()
        req.predicate = NSPredicate(format: "id = %@", id)
        return req
    }
    
    @NSManaged public var applicationName: String?
    @NSManaged private var attachmentsData: Data?
    @NSManaged private var bookmarkedInternal: Bool
    @NSManaged public var cardData: Data?
    @NSManaged public var content: String
    @NSManaged public var createdAt: Date
    @NSManaged public var editedAt: Date?
    @NSManaged private var emojisData: Data?
    @NSManaged public var favourited: Bool
    @NSManaged public var favouritesCount: Int
    @NSManaged private var hashtagsData: Data?
    @NSManaged public var id: String
    @NSManaged public var inReplyToAccountID: String?
    @NSManaged public var inReplyToID: String?
    @NSManaged private var mentionsData: Data?
    @NSManaged public var muted: Bool
    @NSManaged private var pinnedInternal: Bool
    @NSManaged public var reblogged: Bool
    @NSManaged public var reblogsCount: Int
    @NSManaged public var sensitive: Bool
    @NSManaged public var spoilerText: String
    @NSManaged public var uri: String // todo: are both uri and url necessary?
    @NSManaged public var url: URL?
    @NSManaged private var visibilityString: String
    @NSManaged private var pollData: Data?
    @NSManaged public var account: AccountMO
    @NSManaged public var reblog: StatusMO?
    @NSManaged public var localOnly: Bool
    @NSManaged public var lastFetchedAt: Date?
    
    @LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
    public var attachments: [Attachment]

    @LazilyDecoding(arrayFrom: \StatusMO.emojisData)
    public var emojis: [Emoji]

    @LazilyDecoding(arrayFrom: \StatusMO.hashtagsData)
    public var hashtags: [Hashtag]
    
    @LazilyDecoding(arrayFrom: \StatusMO.mentionsData)
    public var mentions: [Mention]
    
    @LazilyDecoding(from: \StatusMO.cardData, fallback: nil)
    public var card: Card?
    
    @LazilyDecoding(from: \StatusMO.pollData, fallback: nil)
    public var poll: Poll?
    
    public var pinned: Bool? { pinnedInternal }
    public var bookmarked: Bool? { bookmarkedInternal }
    
    public var visibility: Pachyderm.Visibility {
        get {
            Pachyderm.Visibility(rawValue: visibilityString) ?? .public
        }
        set {
            visibilityString = newValue.rawValue
        }
    }
    
    public override func awakeFromFetch() {
        super.awakeFromFetch()
        
        managedObjectContext?.perform {
            self.touch()
        }
    }
    
    /// Update the `lastFetchedAt` date so this object isn't pruned early.
    func touch() {
        lastFetchedAt = Date()
    }
    
}

extension StatusMO {
    convenience init(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore, context: NSManagedObjectContext) {
        self.init(context: context)
        self.updateFrom(apiStatus: status, container: container)
        self.lastFetchedAt = Date()
    }
    
    func updateFrom(apiStatus status: Pachyderm.Status, container: MastodonCachePersistentStore) {
        guard let context = managedObjectContext else {
            // we have been deleted, don't bother updating
            return
        }
        
        self.applicationName = status.application?.name
        self.attachments = status.attachments
        self.bookmarkedInternal = status.bookmarked ?? false
        self.card = status.card
        self.content = status.content
        self.createdAt = status.createdAt
        self.editedAt = status.editedAt
        self.emojis = status.emojis
        self.favourited = status.favourited ?? false
        self.favouritesCount = status.favouritesCount
        self.hashtags = status.hashtags
        self.inReplyToAccountID = status.inReplyToAccountID
        self.inReplyToID = status.inReplyToID
        self.id = status.id
        self.mentions = status.mentions
        self.muted = status.muted ?? false
        self.pinnedInternal = status.pinned ?? false
        self.reblogged = status.reblogged ?? false
        self.reblogsCount = status.reblogsCount
        self.sensitive = status.sensitive
        self.spoilerText = status.spoilerText
        self.uri = status.uri
        self.url = status.url != nil ? URL(status.url!) : nil
        self.visibility = status.visibility
        self.poll = status.poll
        self.localOnly = status.localOnly ?? false
        
        if let existing = container.account(for: status.account.id, in: context) {
            existing.updateFrom(apiAccount: status.account, container: container)
            self.account = existing
        } else {
            self.account = AccountMO(apiAccount: status.account, container: container, context: context)
        }
        if let reblog = status.reblog {
            if let existing = container.status(for: reblog.id, in: context) {
                existing.updateFrom(apiStatus: reblog, container: container)
                self.reblog = existing
            } else {
                self.reblog = StatusMO(apiStatus: reblog, container: container, context: context)
            }
        } else {
            self.reblog = nil
        }
    }
}
