import {appPath, Cache, Config, fetch, hasOwnProperty, Inject, Singleton} from '@extollo/lib'
import {MarkMark} from '../../markmark/types'
import {Parser} from '../../markmark/parser'
import {HtmlRenderer} from '../../markmark/html.renderer'
import {MarkMarkRenderer} from '../../markmark/markmark.renderer'

@Singleton()
export class MarkMarkService {
    @Inject()
    protected readonly config!: Config

    @Inject()
    protected readonly cache!: Cache

    private cachedLinks?: MarkMark
    private cachedLinksHTML?: string
    private cachedLinksMM?: string

    public async getLinks(): Promise<MarkMark> {
        if ( this.cachedLinks ) {
            return this.cachedLinks
        }

        let content = await this.cache.fetch('www-markmark-content')
        if ( !content || this.config.get('server.debug') ) {
            content = await this.readFromOutline()
            const expDateEpoch = (new Date).getTime() + (60 * 60 * 1000)  // cache for 1hr
            await this.cache.put('www-markmark-content', content, new Date(expDateEpoch))
        }

        return (this.cachedLinks = (new Parser()).parse(content))
    }

    private async readFromFile(): Promise<string> {
        const path = appPath('resources', 'markmark', 'links.mark.md')
        return path.read()
    }

    private async readFromOutline(): Promise<string> {
        const apiUrl = this.config.safe('server.outline.apiUrl').string()
        const apiKey = this.config.safe('server.outline.apiKey').string()
        const docId = this.config.safe('server.outline.markmarkDocumentId').string()

        const response = await fetch(`${apiUrl}/documents.info`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`,
            },
            body: JSON.stringify({
                id: docId,
            }),
        })

        if ( !response.ok ) {
            throw new Error('Unable to load bookmarks from Outline: response was not okay')
        }

        const body = await response.json()
        if (
            typeof body === 'object' && body
            && hasOwnProperty(body, 'data')
            && typeof body.data === 'object' && body.data
            && hasOwnProperty(body.data, 'text')
            && typeof body.data.text === 'string'
        ) {
            // the frontmatter gets escaped by Outline, so fix it
            return body.data.text
                .replaceAll('\\[//\\]:', '[//]:')
                .split('\n')
                .map(line => {
                    if (line.trim()
                        .startsWith('[//]:')) {
                        return line.replaceAll('\\n', '\n')
                    }
                    return line
                })
                .join('\n')
        }

        throw new Error('Unable to load bookmarks from Outline: invalid body content')
    }

    public async getLinksHTML(): Promise<string> {
        if ( this.cachedLinksHTML ) {
            return this.cachedLinksHTML
        }

        const mm = await this.getLinks()
        return (this.cachedLinksHTML = (new HtmlRenderer()).render(mm))
    }

    public async getLinksMM(): Promise<string> {
        if ( this.cachedLinksMM ) {
            return this.cachedLinksMM
        }

        const mm = await this.getLinks()
        return (this.cachedLinksMM = (new MarkMarkRenderer()).render(mm))
    }
}
