import {
    Controller,
    view,
    Injectable,
    SecurityContext,
    Inject,
    Collection,
    Config,
    Routing,
    file,
    Application,
    make,
    Valid, Logging, api, Session, plaintext,
} from '@extollo/lib'
import {WorkItem} from '../../models/WorkItem.model'
import {FeedPost} from '../../models/FeedPost.model'
import {ContactForm} from '../../types/ContactForm.type'
import {ContactSubmission} from '../../models/ContactSubmission.model'
import {Gotify} from 'gotify'
import {ColorPalette} from '../../configs/app.config'

@Injectable()
export class Home extends Controller {
    @Inject()
    protected readonly security!: SecurityContext

    @Inject()
    protected readonly config!: Config

    @Inject()
    protected readonly routing!: Routing

    @Inject()
    protected readonly gotify!: Gotify

    @Inject()
    protected readonly logging!: Logging

    @Inject()
    protected readonly session!: Session

    public async welcome() {
        return view('welcome')
    }

    public async welcome70s(feedPosts: Collection<FeedPost>) {
        const workItems = await this.getWorkItems()

        const workItemYears = workItems.map(item => item.startDate.getFullYear()).unique()
        const workItemVisibleYears = workItems.filter(item => !item.endDate)
            .map(item => item.startDate.getFullYear())
            .unique()

        return view('welcome_70s', {
            ...this.getThemeCSS(),
            feedPosts: feedPosts.toArray(),
            workItemGroups: workItems.groupBy(item => item.startDate.getFullYear()),
            workItemYears: workItemYears.toArray(),
            workItemHiddenYears: workItemYears.diff(workItemVisibleYears)
        })
    }

    public technical() {
        const isOptOut = this.request.cookies.has(this.config.get('app.analytics.optOutCookie'))

        return view('technical', {
            title: 'Technical Info',
            isOptOut,
            ...this.getThemeCSS(),
        })
    }

    public style() {
        return view('style', {
            title: 'Style Test',
            ...this.getThemeCSS(),
        })
    }

    public optOutPrompt() {
        return view('message', {
            title: 'Analytics Opt-Out',
            message: 'This will set a cookie to disable analytics recording in your browser on all <code>*.garrettmills.dev</code> sites. Continue?',
            buttonAction: this.routing.getNamedPath('opt-out').toRemote,
            buttonMethod: 'post',
            ...this.getThemeCSS(),
        })
    }

    public optOut() {
        this.request.cookies.set(
            this.config.get('app.analytics.optOutCookie'),
            true,
            {
                expires: new Date(Date.now() + (1000 * 60 * 60 * 24 * 365 * 100)),
                secure: true,
                httpOnly: true,
                path: '/',
            },
        )

        return view('message', {
            title: 'Analytics Opt-Out',
            message: 'You have been opted-out of analytics recording.',
            buttonAction: this.routing.getNamedPath('home').toRemote,
            ...this.getThemeCSS(),
        })
    }

    protected async getWorkItems(): Promise<Collection<WorkItem>> {
        const query = WorkItem.query<WorkItem>()
            .orderByDescending('start_date')

        if ( !this.security.hasUser() ) {
            query.where('visible', '=', true)
        }

        return query.get().collect()
    }

    favicon() {
        return file(
            Application.getApplication()
                .appPath('resources', 'assets', 'favicon', 'favicon.ico')
        )
    }

    humans() {
        return Application.getApplication()
            .appPath('resources', 'assets', 'humans.txt')
            .read()
    }

    async contact(data: ContactForm) {
        await this.sendContact(data)
        return view('message', {
            title: 'Message Sent',
            message: 'Your message has been sent. Thanks! I\'ll be in touch soon.',
            buttonAction: this.routing.getNamedPath('home').toRemote,
            ...this.getThemeCSS(),
        })
    }

    async contactApi(data: ContactForm) {
        await this.sendContact(data)
        return api.one({})
    }

    protected async sendContact(data: ContactForm) {
        const submission = make<ContactSubmission>(ContactSubmission)
        submission.name = data.name
        submission.email = data.email
        submission.message = data.message
        await submission.save()

        this.gotify.send({
            app: this.config.get('gotify.app'),
            title: `Contact form submission from ${data.name}`,
            priority: 5,
            message: [
                `From: ${data.name}`,
                `E-mail: ${data.email}`,
                'Message:',
                data.message,
            ].join('\n'),
        }).then(x => this.logging.debug(x))
            .catch(e => this.logging.error(e))
    }

    public getThemeCSSFile() {
        let key = String(this.request.input('themeKey') || '')
        if ( key.endsWith('.css') ) key = key.substring(0, key.length - 4)
        const themes = this.config.get('app.colors') as Record<string, ColorPalette>
        const themeKeys = Object.keys(themes)
        const theme = themes[key] || themes[themeKeys[0]]
        const themeCSS = `
            /* Theme: ${theme.displayName} */
            :root {
                --c-background: ${theme.background};
                --c-background-offset: ${theme.backgroundOffset};
                --c-background-offset-2: ${theme.backgroundOffset2};
                --c-hero: ${theme.hero};
                --c-font: ${theme.font};
                --c-font-muted: ${theme.fontMuted};
                --c-box: ${theme.box};
                --c-link: ${theme.link};
                --c-noise-size: ${theme.noiseSize};
                --c-line-1: ${theme.line1};
                --c-line-2: ${theme.line2};
                --c-line-3: ${theme.line3};
            }
        `
        return plaintext(themeCSS).contentType('text/css')
    }

    public getThemeCSS(): any {
        const themes = this.config.get('app.colors') as Record<string, ColorPalette>
        const themeKeys = Object.keys(themes)
        const themeName = this.session.get('theme.name')
        // const themeName = this.request.safe('theme').or(themeKeys[Math.floor(Math.random()*themeKeys.length)]).in(themeKeys)
        const theme = themes[themeName] || themes[themeKeys[0]]
        const themeCSS = `
            :root {
                --c-background: ${theme.background};
                --c-background-offset: ${theme.backgroundOffset};
                --c-background-offset-2: ${theme.backgroundOffset2};
                --c-hero: ${theme.hero};
                --c-font: ${theme.font};
                --c-font-muted: ${theme.fontMuted};
                --c-box: ${theme.box};
                --c-link: ${theme.link};
                --c-noise-size: ${theme.noiseSize};
                --c-line-1: ${theme.line1};
                --c-line-2: ${theme.line2};
                --c-line-3: ${theme.line3};
            }
        `

        const themeStylesheets = themeKeys
            .filter(key => key !== themeName)
            .map(key => ({
                key,
                displayName: themes[key].displayName,
                url: `/theme/${key}.css`,
            }))

        return {
            themeName,
            themeCSS: '',
            themeDisplayName: theme.displayName,
            themeKeys,
            themeStylesheets,
            themeRecord: theme,
            textIsLight: theme.textVariant === 'light',
        }
    }
}
