import Ink

extension PageGenerationResults: ItemLinkResults {

}

struct MarkdownLinkProcessor: MarkdownProcessor {

    static let modifier: Modifier.Target = .links

    private let content: Content

    private let results: ItemLinkResults

    private let language: ContentLanguage

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

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

    func process(html: String, markdown: Substring) -> String {
        let markdownUrl = markdown.between("(", and: ")")
        guard let (textToReplace, url) = convert(markdownUrl: markdownUrl) else {
            // Keep original code, no changes needed
            return html
        }
        guard let convertedUrl = url else {
            // Remove link since the target can't be found
            return markdown.between("[", and: "]")
        }
        return html.replacingOccurrences(of: textToReplace, with: convertedUrl)
    }

    private let pageLinkMarker = "page:"

    private let tagLinkMarker = "tag:"

    private let fileLinkMarker = "file:"

    func convert(markdownUrl: String) -> (textToChange: String, url: String?)? {
        if let result = handle(prefix: pageLinkMarker, in: markdownUrl, handler: convert(inlinePageLink:)) {
            return result
        }
        if let result = handle(prefix: tagLinkMarker, in: markdownUrl, handler: convert(inlineTagLink:)) {
            return result
        }
        if let result = handle(prefix: fileLinkMarker, in: markdownUrl, handler: convert(inlineFileLink:)) {
            return result
        }
        results.externalLink(to: markdownUrl)
        // No need to change anything
        return nil
    }

    private func handle(prefix: String, in markdownUrl: String, handler: (String) -> String?)  -> (textToChange: String, url: String?)? {
        guard markdownUrl.hasPrefix(prefix) else {
            // Continue search
            return nil
        }
        let inlineLink = markdownUrl.replacingOccurrences(of: prefix, with: "")
        let itemId = inlineLink.dropAfterFirst("#")
        let url = handler(itemId)
        return (textToChange: prefix + itemId, url: url)
    }

    private func convert(inlinePageLink pageId: String) -> String? {
        guard let page = content.page(pageId) else {
            results.missing(page: pageId, source: "Inline page link")
            // Remove link since the page can't be found
            return nil
        }
        // Note link even for draft pages
        results.linked(to: page)

        guard !page.isDraft else {
            // TODO: Report links to draft pages
            return nil
        }
        return page.absoluteUrl(in: language)
    }

    private func convert(inlineTagLink: String) -> String? {
        let tagId = inlineTagLink.dropAfterFirst("#")
        guard let tag = content.tag(tagId) else {
            results.missing(tag: tagId, source: "Inline tag link")
            // Remove link since the tag can't be found
            return nil
        }
        results.linked(to: tag)
        return tag.absoluteUrl(in: language)
    }

    private func convert(inlineFileLink fileId: String) -> String? {
        guard let file = content.file(fileId) else {
            results.missing(file: fileId, source: "Inline file link")
            // Remove link since the file can't be found
            return nil
        }
        results.require(file: file)
        return file.absoluteUrl
    }
}
