import {Inject, Injectable, Maybe, Field, ORMUser, FieldType, Routing} from '@extollo/lib'
import {Pub} from '../../pub/types'
import {Certificate} from './pub/Certificate.model'
import * as child_process from 'child_process'

@Injectable()
export class User extends ORMUser {
    @Inject()
    protected readonly routing!: Routing

    @Field(FieldType.varchar)
    public tagline?: string

    @Field(FieldType.varchar, 'photo_url')
    public photoUrl?: string

    get pubUrl(): string {
        return `${this.routing.getAppUrl().toRemote}/pub/${this.username}`
    }

    get initials(): string {
        return `${this.firstName || ''} ${this.lastName || ''}`
            .trim()
            .split(/\s+/)
            .map(s => s[0])
            .join('')
            .toUpperCase()
    }

    async toWebfinger(): Promise<Pub.Webfinger> {
        const host = new URL(this.routing.getAppUrl().toRemote).host
        return {
            subject: `acct:${this.username}@${host}`,
            aliases: [],
            links: [
                {
                    rel: 'self',
                    type: 'application/activity+json',
                    href: this.pubUrl,
                },
                {
                    rel: 'http://webfinger.net/rel/profile-page',
                    type: 'text/html',
                    href: `${this.pubUrl}/web`,
                },
            ],
        }
    }

    async toPub(): Promise<Pub.Actor> {
        return {
            "@context": [
                "https://www.w3.org/ns/activitystreams",
                "https://w3id.org/security/v1"
            ],
            id: this.pubUrl,
            type: 'Person',
            name: `${this.firstName ? this.firstName + ' ' : ''}${this.lastName || ''}`.trim() || this.username,
            url: `${this.pubUrl}/web`,
            discoverable: true,
            indexable: false,
            published: '2023-11-06T00:00:00.000Z',  // FIXME
            ...(this.tagline ? {summary: this.tagline} : {}),
            ...(this.photoUrl ? {icon: this.photoUrl} : {}),
            // image: '',  // FIXME
            preferredUsername: this.username,
            manuallyApprovesFollowers: false,
            inbox: `${this.pubUrl}/inbox`,
            publicKey: {
                id: `${this.pubUrl}#main-key`,
                owner: this.pubUrl,
                publicKeyPem: await this.getCertificate().then(c => c.pubkey),
            },
        }
    }

    certificate(): Promise<Maybe<Certificate>> {
        return Certificate.query<Certificate>()
            .where('reltype', '=', 'user')
            .where('relid', '=', this.userId)
            .first()
    }

    async getCertificate(): Promise<Certificate> {
        const existing = await this.certificate()
        if ( existing ) {
            return existing
        }

        const certificate = this.make<Certificate>(Certificate)
        certificate.reltype = 'user'
        certificate.relid = this.userId
        certificate.privkey = await this.exec('openssl genrsa -out - 2048')
        certificate.pubkey = await this.exec(`echo "${certificate.privkey}" | openssl rsa -outform PEM -pubout -out -`)
        await certificate.save()

        return certificate
    }

    /** Dirty, dirty, dirty. @fixme */
    private async exec(cmd: string): Promise<string> {
        return new Promise<string>((res, rej) => {
            child_process.exec(cmd, (err, stdout, stderr) => {
                if ( err ) {
                    return rej(err)
                }

                res(stdout.trim())
            })
        })
    }
}
