//
//  MastodonStatusContent+ParseResult.swift
//  Mastodon
//
//  Created by Cirno MainasuK on 2021-6-20.
//

import Foundation
import ActiveLabel

extension MastodonStatusContent {
    struct ParseResult: Hashable {
        let document: String
        let original: String
        let trimmed: String
        let activeEntities: [ActiveEntity]

        static func == (lhs: MastodonStatusContent.ParseResult, rhs: MastodonStatusContent.ParseResult) -> Bool {
            return lhs.document == rhs.document
                && lhs.original == rhs.original
                && lhs.trimmed == rhs.trimmed
                && lhs.activeEntities.count == rhs.activeEntities.count     // FIXME:
        }

        func hash(into hasher: inout Hasher) {
            hasher.combine(document)
            hasher.combine(original)
            hasher.combine(trimmed)
            hasher.combine(activeEntities.count)        // FIXME:
        }

        func trimmedAttributedString(appearance: MastodonStatusContent.Appearance) -> NSAttributedString {
            let attributedString = NSMutableAttributedString(string: trimmed, attributes: appearance.attributes)
            for entity in activeEntities {
                switch entity.type {
                case .url:
                    attributedString.addAttributes(appearance.urlAttributes, range: entity.range)
                case .hashtag:
                    attributedString.addAttributes(appearance.hashtagAttributes, range: entity.range)
                case .mention:
                    attributedString.addAttributes(appearance.mentionAttributes, range: entity.range)
                default:
                    break
                }
                if let uri = entity.type.uri {
                    attributedString.addAttributes([
                        .link: uri
                    ], range: entity.range)
                }
            }
            return attributedString
        }
    }
}

extension ActiveEntityType {

    static let appScheme = "mastodon"

    init?(url: URL) {
        guard let scheme = url.scheme?.lowercased() else { return nil }
        guard scheme == ActiveEntityType.appScheme else {
            self = .url("", trimmed: "", url: url.absoluteString, userInfo: nil)
            return
        }

        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
              let parameters = components.queryItems else { return nil }

        if let hashtag = parameters.first(where: { $0.name == "hashtag" }), let encoded = hashtag.value, let value = String(base64Encoded: encoded) {
            self = .hashtag(value, userInfo: nil)
            return
        }
        if let mention = parameters.first(where: { $0.name == "mention" }), let encoded = mention.value, let value = String(base64Encoded: encoded) {
            self = .mention(value, userInfo: nil)
            return
        }
        return nil
    }

    var uri: URL? {
        switch self {
        case .url(_, _, let url, _):
            return URL(string: url)
        case .hashtag(let hashtag, _):
            return URL(string: "\(ActiveEntityType.appScheme)://meta?hashtag=\(hashtag.base64Encoded)")
        case .mention(let mention, _):
            return URL(string: "\(ActiveEntityType.appScheme)://meta?mention=\(mention.base64Encoded)")
        default:
            return nil
        }
    }

}

extension String {
    fileprivate var base64Encoded: String {
        return Data(self.utf8).base64EncodedString()
    }

    init?(base64Encoded: String) {
        guard let data = Data(base64Encoded: base64Encoded),
              let string = String(data: data, encoding: .utf8) else {
            return nil
        }
        self = string
    }
}
