//
//  Poll.swift
//  CoreDataStack
//
//  Created by MainasuK Cirno on 2021-3-2.
//

import Foundation
import CoreData

public final class Poll: NSManagedObject {
    public typealias ID = String
    
    @NSManaged public private(set) var id: ID
    @NSManaged public private(set) var expiresAt: Date?
    @NSManaged public private(set) var expired: Bool
    @NSManaged public private(set) var multiple: Bool
    @NSManaged public private(set) var votesCount: NSNumber
    @NSManaged public private(set) var votersCount: NSNumber?
    
    @NSManaged public private(set) var createdAt: Date
    @NSManaged public private(set) var updatedAt: Date
    
    // one-to-one relationship
    @NSManaged public private(set) var status: Status
    
    // one-to-many relationship
    @NSManaged public private(set) var options: Set<PollOption>
    
    // many-to-many relationship
    @NSManaged public private(set) var votedBy: Set<MastodonUser>?
}

extension Poll {
    
    public override func awakeFromInsert() {
        super.awakeFromInsert()
        setPrimitiveValue(Date(), forKey: #keyPath(Poll.createdAt))
    }
    
    @discardableResult
    public static func insert(
        into context: NSManagedObjectContext,
        property: Property,
        votedBy: MastodonUser?,
        options: [PollOption]
    ) -> Poll {
        let poll: Poll = context.insertObject()
        
        poll.id = property.id
        poll.expiresAt = property.expiresAt
        poll.expired = property.expired
        poll.multiple = property.multiple
        poll.votesCount = property.votesCount
        poll.votersCount = property.votersCount
        
        
        poll.updatedAt = property.networkDate
        
        if let votedBy = votedBy {
            poll.mutableSetValue(forKey: #keyPath(Poll.votedBy)).add(votedBy)            
        }
        poll.mutableSetValue(forKey: #keyPath(Poll.options)).addObjects(from: options)
        
        return poll
    }
    
    public func update(expiresAt: Date?) {
        if self.expiresAt != expiresAt {
            self.expiresAt = expiresAt
        }
    }
    
    public func update(expired: Bool) {
        if self.expired != expired {
            self.expired = expired
        }
    }
    
    public func update(votesCount: Int) {
        if self.votesCount.intValue != votesCount {
            self.votesCount = NSNumber(value: votesCount)
        }
    }
    
    public func update(votersCount: Int?) {
        if self.votersCount?.intValue != votersCount {
            self.votersCount = votersCount.flatMap { NSNumber(value: $0) }
        }
    }
    
    public func update(voted: Bool, by: MastodonUser) {
        if voted {
            if !(votedBy ?? Set()).contains(by) {
                mutableSetValue(forKey: #keyPath(Poll.votedBy)).add(by)
            }
        } else {
            if (votedBy ?? Set()).contains(by) {
                mutableSetValue(forKey: #keyPath(Poll.votedBy)).remove(by)
            }
        }
    }
    
    public func didUpdate(at networkDate: Date) {
        self.updatedAt = networkDate
    }
    
}

extension Poll {
    public struct Property {
        public let id: ID
        public let expiresAt: Date?
        public let expired: Bool
        public let multiple: Bool
        public let votesCount: NSNumber
        public let votersCount: NSNumber?
        
        public let networkDate: Date

        public init(
            id: Poll.ID,
            expiresAt: Date?,
            expired: Bool,
            multiple: Bool,
            votesCount: Int,
            votersCount: Int?,
            networkDate: Date
        ) {
            self.id = id
            self.expiresAt = expiresAt
            self.expired = expired
            self.multiple = multiple
            self.votesCount = NSNumber(value: votesCount)
            self.votersCount = votersCount.flatMap { NSNumber(value: $0) }
            self.networkDate = networkDate
        }
    }
}

extension Poll: Managed {
    public static var defaultSortDescriptors: [NSSortDescriptor] {
        return [NSSortDescriptor(keyPath: \Poll.createdAt, ascending: false)]
    }
}
