
struct ButtonCommand: CommandProcessor {

    static let commandType: CommandType = .buttons

    let content: Content

    let results: PageGenerationResults

    init(content: Content, results: PageGenerationResults, language: ContentLanguage) {
        self.content = content
        self.results = results
    }

    /**
     Format: `![buttons](type=<specification>;...)`
     Types:
     - Download: `download=<fileId>,<text>,<download-filename?>`
     - External link: `external=<url>,<text>`
     - Git: `git=<url>,<text>`
     - Play: `play-circle=<text>,<click-action>`
     */
    func process(_ arguments: [String], markdown: Substring) -> String {
        let buttons = arguments.compactMap { convert(button: $0, markdown: markdown) }
        return ContentButtons(items: buttons).content
    }

    private func convert(button: String, markdown: Substring) -> ContentButtons.Item? {
        guard let type = PageIcon(rawValue: button.dropAfterFirst("=").trimmed) else {
            invalid(markdown)
            return nil
        }
        let parts = button.dropBeforeFirst("=").components(separatedBy: ",").map { $0.trimmed }
        switch type {
        case .buttonDownload:
            return download(arguments: parts, markdown: markdown)
        case .buttonGitLink:
            return link(icon: .buttonGitLink, arguments: parts, markdown: markdown)
        case .buttonExternalLink:
            return link(icon: .buttonExternalLink, arguments: parts, markdown: markdown)
        case .buttonPlay:
            return play(arguments: parts, markdown: markdown)
        default:
            invalid(markdown)
            return nil
        }
    }

    private func download(arguments: [String], markdown: Substring) -> ContentButtons.Item? {
        guard (2...3).contains(arguments.count) else {
            invalid(markdown)
            return nil
        }
        let fileId = arguments[0].trimmed
        let title = arguments[1].trimmed
        let downloadName = arguments.count > 2 ? arguments[2].trimmed : nil

        guard let file = content.file(fileId) else {
            results.missing(file: fileId, source: "Download button")
            return nil
        }
        results.require(file: file)
        results.require(icon: .buttonDownload)
        return ContentButtons.Item(
            icon: .buttonDownload,
            filePath: file.absoluteUrl,
            text: title,
            downloadFileName: downloadName)
    }

    private func link(icon: PageIcon, arguments: [String], markdown: Substring) -> ContentButtons.Item? {
        guard arguments.count == 2 else {
            results.invalid(command: .buttons, markdown)
            return nil
        }
        let rawUrl = arguments[0].trimmed
        guard let url = rawUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
            results.invalid(command: .buttons, markdown)
            return nil
        }

        results.externalLink(to: rawUrl)
        results.require(icon: icon)

        let title = arguments[1].trimmed

        return .init(icon: icon, filePath: url, text: title)
    }

    private func play(arguments: [String], markdown: Substring) -> ContentButtons.Item? {
        guard arguments.count == 2 else {
            results.invalid(command: .buttons, markdown)
            return nil
        }
        let text = arguments[0].trimmed
        let event = arguments[1].trimmed

        results.require(icon: .buttonPlay)

        return .init(icon: .buttonPlay, filePath: nil, text: text, onClickText: event)
    }
}
