import {
    Config,
    Controller,
    HTTPError,
    HTTPStatus,
    Inject,
    Injectable,
    Logging,
    Maybe,
    ResponseObject,
    Routing,
    SecurityContext,
    view,
} from '@extollo/lib'
import {User} from '../../models/User.model'
import {one} from '@extollo/lib/lib/http/response/api'
import {ResourceAction, ResourceConfiguration} from '../../cobalt'

@Injectable()
export class Dash2 extends Controller {
    @Inject()
    protected readonly logging!: Logging

    @Inject()
    protected readonly routing!: Routing

    @Inject()
    protected readonly security!: SecurityContext

    @Inject()
    protected readonly config!: Config

    public async index(): Promise<ResponseObject> {
        return view('dash2:home', {
            title: 'Home',
            ...(await this.appData()),
        })
    }

    public async cobalt(): Promise<ResponseObject> {
        return view('dash2:cobalt', {
            ...(await this.appData()),
        })
    }

    public async cobaltSettings(): Promise<ResponseObject> {
        const layout = this.request.safe('layoutname').string()
        const params: Record<string, unknown> = this.request.input('params') as any || {}

        if ( layout === 'resource:form' && typeof params.resource === 'string' ) {
            return this.getResourceFormSettings(params.resource, params)
        }

        if ( layout === 'resource:list' && typeof params.resource === 'string' ) {
            return this.getResourceListSettings(params.resource, params)
        }

        return one({
            layout,
            params,
        })
    }

    public async cobaltResourceList(): Promise<ResponseObject> {
        return view('dash2:cobalt', {
            ...(await this.appData()),
            layout: 'resource:list',
            params: { resource: this.request.safe('key').string() },
        })
    }

    private async getResourceListSettings(resourceName: string, params: Record<string, unknown>): Promise<ResponseObject> {
        const config = this.getResourceConfigOrFail(resourceName)

        return one({
            actions: {
                top: [
                    {
                        type: 'navigate',
                        route: `/dash2/cobalt/resource/${resourceName}/form`,
                        title: 'Create New',
                        color: '#94AE89',
                        icon: 'fa-solid fa-plus',
                    },
                ],
                inline: [
                    {
                        type: 'navigate',
                        route: `/dash2/cobalt/resource/${resourceName}/form`,
                        title: 'Open',
                        color: '#50808E',
                        icon: 'fa-solid fa-eye',
                        // fixme: gather
                    },
                    {
                        type: 'route',
                        route: `/dash2/cobalt/resource/${resourceName}/form`,
                        method: 'delete',
                        title: 'Delete',
                        color: '#B02E0C',
                        icon: 'fa-solid fa-trash-can',
                        // fixme: gather
                    },
                ],
            },
            loadActions: [
                {
                    target: {
                        sourceName: `resource:list:${resourceName}`,
                    },
                    action: {
                        type: 'route',
                        method: 'GET',
                        route: `/dash/cobalt/resource/${resourceName}`,
                        successKey: 'success',
                        resultKey: 'data',
                    },
                },
            ],
            layoutChildren: [
                {
                    component: 'table',
                    config: {
                        sourceName: `resource:list:${resourceName}`,
                        rowKey: 'records',
                        fields: config.fields,
                    },
                },
            ],
        })
    }

    private async getResourceFormSettings(resourceName: string, params: Record<string, unknown>): Promise<ResponseObject> {
        const config = this.getResourceConfigOrFail(resourceName)

        return one({
            layoutChildren: [
                {
                    component: 'card',
                    config: {
                        display: config.display.singular,
                    },
                    layoutChildren: [
                        {
                            component: 'form',
                            sourceName: `resource:form:${resourceName}`,
                            layoutChildren: config.fields
                                .filter(f => !f.hideOn?.form)
                                .map(f => ({
                                    component: 'field',
                                    config: f,
                                }))
                        },
                    ],
                },
            ],
        })
    }

    public async appData() {
        const resourceConfigs = this.config.get('cobalt.resources', []) as ResourceConfiguration[]
        const user = this.security.user() as User
        return {
            appData: {
                appUrl: this.routing.getAppUrl().toRemote,
                user: {
                    firstName: user.firstName,
                    lastName: user.lastName,
                    username: user.username,
                    photoUrl: user.photoUrl,
                    initials: user.initials,
                },
            },
            resources: resourceConfigs
                .filter(c => c.supportedActions.includes(ResourceAction.read))
                .map(c => ({
                    display: c.display.plural,
                    href: `/dash2/cobalt/resource/${c.key}/list`,
                })),
        }
    }

    protected getResourceConfigOrFail(key: string): ResourceConfiguration {
        const config = this.getResourceConfig(key)
        if ( !config ) {
            throw new HTTPError(HTTPStatus.NOT_FOUND)
        }
        return config
    }

    protected getResourceConfig(key: string): Maybe<ResourceConfiguration> {
        const configs = this.config.get('cobalt.resources') as ResourceConfiguration[]
        for ( const config of configs ) {
            if ( config.key === key ) {
                return config
            }
        }
    }
}
