import {AssetExporterPlugin, AViewerPluginSync, IObject3D, uiButton, uiFolderContainer, uiInput} from 'threepipe'

/**
 * Transfr Share Plugin
 * A sample plugin that provides helpers to export and upload scene to a server and get a shareable link.
 * It uses the options from the {@link AssetExporterPlugin} to export the scene or object, and can be configured using it's ui.
 *
 * Uses the free service [transfr.one](https://transfr.one/) by default which deletes the files after a certain time,
 * but the url can be changed to a custom backend or a self-hosted version of transfr.
 *
 * Note: since the uploaded files are publicly accessible by anyone by default, it is recommended to encrypt the file using the exporter options or use a secure backend.
 */
@uiFolderContainer('Share Link')
export class TransfrSharePlugin extends AViewerPluginSync<''> {
    public static readonly PluginType = 'TransfrSharePlugin'

    toJSON: any = null
    enabled = true

    dependencies = [AssetExporterPlugin]

    @uiInput('Server URL')
        serverUrl = 'https://bee.transfr.one/scene.glb'
    @uiInput()
        queryParam = 'm'
    @uiInput()
        pageUrl = window.location.href

    baseUrls: Record<string, string> = {
        'editor': '',
        'viewer': '',
    }

    async exportObject(model?: IObject3D) {
        const exporter = this._viewer?.getPlugin(AssetExporterPlugin)
        if (!this._viewer) throw new Error('TransfrSharePlugin: AssetExporter not found')
        return model ?
            this._viewer.export(model, exporter?.exportOptions ?? {format: 'glb'}) :
            this._viewer.exportScene(exporter?.exportOptions ?? {})
    }

    /**
     * Export and get the link  of the 3d model or scene
     * @param model
     */
    async getLink(model?: IObject3D) {
        const obj = await this.exportObject(model)
        if (!obj) {
            throw new Error('Failed to export object or scene')
        }
        const path = 'transfr.one/scene.glb'
        this._viewer!.assetManager.setProcessState(path, {
            state: 'Uploading',
            // progress: data.progress ? data.progress * 100 : undefined,
        })
        const res = await fetch(this.serverUrl, {
            method: 'PUT',
            body: obj,
        })
        if (res.status !== 200) {
            throw new Error('Failed to upload file')
        }
        const data = (await res.text())?.trim()
        this._viewer!.assetManager.setProcessState(path, undefined)
        // console.log(data)
        try {
            new URL(data)
        } catch (e) {
            throw new Error('Invalid URL ' + data)
        }
        return data
    }
    private _exporting = false

    /**
     * Upload the scene and copy the link to clipboard along with the base url and query param if provided
     * @param base
     * @param param
     */
    async shareLink(base?: string|URL, param?: string) {
        if (this._exporting) return null
        this._exporting = true
        let link = await this.getLink().catch(e=>{
            this._viewer?.console.error(e)
            this._viewer?.dialog.alert('Error: Failed to share scene: \n' + e.message)
            return null
        })

        if (link) {
            if (base) {
                const url = typeof base === 'string' ?
                    new URL(this.baseUrls[base] ?? base) : base
                url.searchParams.set(param || this.queryParam || 'm', link)
                link = url.href
            }
            let copied = false
            try {
                if (window && window.navigator && navigator.clipboard) {
                    await navigator.clipboard.writeText(link)
                    copied = true
                }
            } catch (e) {
                console.error('Failed to copy link', e)
            }
            this._viewer?.dialog.alert('Link' + (copied ? ' Copied' : '') + ': ' + link + '\n\nNote: File will be deleted in 1 days')
        }
        this._exporting = false
        return link
    }

    @uiButton('Share editor link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.baseUrls.editor}))
    async shareEditorLink() {
        return this.shareLink(this.baseUrls.editor, this.queryParam)
    }
    @uiButton('Share viewer link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.baseUrls.viewer}))
    async shareViewerLink() {
        return this.shareLink(this.baseUrls.viewer, this.queryParam)
    }
    @uiButton('Share page link', (t: TransfrSharePlugin)=>({hidden: ()=>!t.pageUrl}))
    async sharePageLink() {
        return this.shareLink(this.pageUrl, this.queryParam)
    }
    @uiButton('Share glb link')
    async shareGlb() {
        return this.shareLink()
    }
}
