import { ErrorView } from '@views/ErrorView'
import { Editor, Notice, Plugin } from 'obsidian'
import { MicroPluginContainerInterface, MicroPluginContainer } from '@base/MicroPluginContainer'
import { MicroPluginSettingsView } from '@views/MicroPluginSettingsView'
import { PublishView } from '@views/PublishView'
import { ServiceFactory, ServiceFactoryInterface } from '@factories/ServiceFactory'
import { StoredSettings, defaultSettings } from '@stores/StoredSettings'
import { TagSynchronizationServiceDelegate, TagSynchronizationServiceInterface } from '@services/TagSynchronizationService'
import { ViewModelFactoryInterface, ViewModelFactory } from '@factories/ViewModelFactory'
import { FrontmatterServiceDelegate, FrontmatterServiceInterface } from './services/FrontmatterService'
import { PublishResponse } from './networking/PublishResponse'

export default class MicroPlugin extends Plugin implements TagSynchronizationServiceDelegate {

    // Properties

    private settings: StoredSettings
    private container: MicroPluginContainerInterface
    private viewModelFactory: ViewModelFactoryInterface
    private serviceFactory: ServiceFactoryInterface
    private synchronizationService: TagSynchronizationServiceInterface
    private frontmatterService: FrontmatterServiceInterface

    // Public

    public async onload() {
        await this.loadSettings()
        await this.loadDependencies()
        await this.loadServiceFactory()
        await this.registerSynchronizationService()
        await this.registerFrontmatterService()
        await this.loadViewModelFactory()

        this.synchronizationService.fetchTags()

        this.addCommand({
            id: 'micropub-publish-command',
            name: 'Post with Micropub',
            editorCallback: (editor, markdownView) => {
                if (editor.getValue().trim().length == 0) {
                    new ErrorView(
                        this.viewModelFactory.makeEmptyPostErrorViewModel()
                    ).open()
                } else {
                    new PublishView(
                        this.viewModelFactory.makePublishViewModel(
                            markdownView.file.basename,
                            editor.getValue(),
                            editor
                        )
                    ).open()
                }
            }
        })

        this.addCommand({
            id: 'micropub-categories-sync-command',
            name: 'Synchronize Categories',
            callback: () => {
                this.synchronizationService.fetchTags()
            }
        })

        this.addSettingTab(
            new MicroPluginSettingsView(
                this.viewModelFactory.makeMicroPluginSettingsViewModel()
            )
        )
    }

    public onunload() { }

    public async saveSettings() {
        await this.saveData(this.settings)
    }

    // Private

    private async loadSettings() {
        this.settings = Object.assign(
            {},
            defaultSettings,
            await this.loadData()
        )
    }

    private async loadDependencies() {
        this.container = new MicroPluginContainer(
            this.settings,
            this
        )
    }

    private async loadViewModelFactory() {
        this.viewModelFactory = new ViewModelFactory(
            this.container,
            this.frontmatterService
        )
    }

    private async loadServiceFactory() {
        this.serviceFactory = new ServiceFactory(
            this.container
        )
    }

    private async registerSynchronizationService() {
        this.synchronizationService = this.serviceFactory
            .makeTagSynchronizationService(
                this
            )
    }

    private async registerFrontmatterService() {
        this.frontmatterService = this.serviceFactory
            .makeFrontmatterService()
    }

    // TagSynchronizationServiceDelegate

    public tagSynchronizationDidSucceed(
        count: number
    ) {
        new Notice(
            'Categories synchronized. Found ' + count + ' categories'
        )
    }

    public tagSynchronizationDidFail(
        error: Error
    ) {
        new Notice(
            'Error synchronizing categories'
        )
    }
}