import Ink

struct MarkdownLinkProcessor: MarkdownProcessor {

    static let modifier: Modifier.Target = .links

    private let content: Content

    private let results: PageGenerationResults

    private let language: ContentLanguage

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

    private let pageLinkMarker = "page:"

    private let tagLinkMarker = "tag:"

    private let fileLinkMarker = "file:"

    func process(html: String, markdown: Substring) -> String {
        let url = markdown.between("(", and: ")")
        if url.hasPrefix(pageLinkMarker) {
            return handleInlinePageLink(url: url, html: html, markdown: markdown)
        }
        if url.hasPrefix(tagLinkMarker) {
            return handleInlineTagLink(url: url, html: html, markdown: markdown)
        }
        if url.hasPrefix(fileLinkMarker) {
            return handleInlineFileLink(url: url, html: html, markdown: markdown)
        }
        results.externalLink(to: url)
        return html
    }

    private func handleInlinePageLink(url: String, html: String, markdown: Substring) -> String {
        // Retain links pointing to elements within a page
        let textToChange = url.dropAfterFirst("#")
        let pageId = textToChange.replacingOccurrences(of: pageLinkMarker, with: "")
        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 markdown.between("[", and: "]")
        }
        guard !page.isDraft else {
            return markdown.between("[", and: "]")
        }

        results.linked(to: page)
        let pagePath = page.absoluteUrl(in: language)
        return html.replacingOccurrences(of: textToChange, with: pagePath)
    }

    private func handleInlineTagLink(url: String, html: String, markdown: Substring) -> String {
        // Retain links pointing to elements within a page
        let textToChange = url.dropAfterFirst("#")
        let tagId = textToChange.replacingOccurrences(of: tagLinkMarker, with: "")
        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 markdown.between("[", and: "]")
        }
        results.linked(to: tag)
        let tagPath = tag.absoluteUrl(in: language)
        return html.replacingOccurrences(of: textToChange, with: tagPath)
    }

    private func handleInlineFileLink(url: String, html: String, markdown: Substring) -> String {
        // Retain links pointing to elements within a page
        let fileId = url.replacingOccurrences(of: fileLinkMarker, with: "")
        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 markdown.between("[", and: "]")
        }
        results.require(file: file)
        let filePath = file.absoluteUrl
        return html.replacingOccurrences(of: url, with: filePath)
    }
}
