import * as path from 'path'

import {
    DynamicModule,
    ForwardReference,
    Global,
    Logger,
    MiddlewareConsumer,
    Module,
    NestModule,
    RequestMethod,
    Type,
} from '@nestjs/common'
import {ConfigModule} from '@nestjs/config'
import {ScheduleModule} from '@nestjs/schedule'
import {AcceptLanguageResolver, CookieResolver, HeaderResolver, I18nModule, QueryResolver} from 'nestjs-i18n'

import {AdminModule} from './api/admins/admin.module'
import {OtpModule} from './api/auth/otp/otp.module'
import {PasswordChangeModule} from './api/auth/password/change/password-change.module'
import {PasswordModule} from './api/auth/password/password.module'
import {BanAdminModule} from './api/bans/admins/ban-admin.module'
import {BanModule} from './api/bans/ban.module'
import {BanIpModule} from './api/bans/ips/ban-ip.module'
import {BanRegistrationModule} from './api/bans/registrations/ban-registration.module'
import {BanSubscriberModule} from './api/bans/subscribers/ban-subscriber.module'
import {ClearCallCounterModule} from './api/clearcallcounters/clear-call-counter.module'
import {ContactModule} from './api/contacts/contact.module'
import {ContractModule} from './api/contracts/contract.module'
import {CustomerContactModule} from './api/customercontacts/customer-contact.module'
import {CustomerBalanceModule} from './api/customers/balances/customer-balance.module'
import {CustomerModule} from './api/customers/customer.module'
import {CustomerPhonebookModule} from './api/customers/phonebook/customer-phonebook.module'
import {CustomerSpeedDialModule} from './api/customerspeeddials/customer-speed-dial.module'
import {DomainModule} from './api/domains/domain.module'
import {FileshareModule} from './api/fileshare/fileshare.module'
import {HeaderManipulationModule} from './api/header-manipulations/header-manipulation.module'
import {HeaderManipulationSetModule} from './api/header-manipulations/sets/header-manipulation-set.module'
import {HeaderManipulationRuleActionModule} from './api/header-manipulations/sets/rules/actions/header-manipulation-rule-action.module'
import {HeaderManipulationRuleConditionModule} from './api/header-manipulations/sets/rules/conditions/header-manipulation-rule-condition.module'
import {HeaderManipulationRuleModule} from './api/header-manipulations/sets/rules/header-manipulation-rule.module'
import {JournalModule} from './api/journals/journal.module'
import {NCOSLevelModule} from './api/ncos/levels/ncos-level.module'
import {NCOSModule} from './api/ncos/ncos.module'
import {NCOSPatternModule} from './api/ncos/patterns/ncos-pattern.module'
import {NCOSSetModule} from './api/ncos-sets/ncos-set.module'
import {NumberModule} from './api/numbers/number.module'
import {PbxGroupMemberModule} from './api/pbx/groups/members/pbx-group-member.module'
import {PbxGroupModule} from './api/pbx/groups/pbx-group.module'
import {PbxModule} from './api/pbx/pbx.module'
import {PbxUserModule} from './api/pbx/users/pbx-user.module'
import {PeeringInboundRuleModule} from './api/peerings/groups/inbound-rules/peering-inbound-rule.module'
import {PeeringGroupModule} from './api/peerings/groups/peering-group.module'
import {PeeringRuleModule} from './api/peerings/groups/rules/peering-rule.module'
import {PeeringGroupServerModule} from './api/peerings/groups/servers/peering-group-server.module'
import {PeeringModule} from './api/peerings/peering.module'
import {ProductModule} from './api/products/product.module'
import {ResellerPhonebookModule} from './api/resellers/phonebook/reseller-phonebook.module'
import {ResellerModule} from './api/resellers/reseller.module'
import {RewriteRuleModule as RewriteModule} from './api/rewrite-rules/rewrite-rule.module'
import {RewriteRuleSetModule} from './api/rewrite-rules/sets/rewrite-rule-set.module'
import {RewriteRuleModule} from './api/rewrite-rules/sets/rules/rewrite-rule.module'
import {SubscriberPhonebookModule} from './api/subscribers/phonebook/subscriber-phonebook.module'
import {SubscriberModule} from './api/subscribers/subscriber.module'
import {SystemContactModule} from './api/systemcontacts/system-contact.module'
import {InvoiceTemplateModule} from './api/templates/invoices/invoice-template.module'
import {TemplateModule} from './api/templates/template.module'
import {VoicemailModule} from './api/voicemails/voicemail.module'
import {AppController} from './app.controller'
import {AppService} from './app.service'
import {AuthModule} from './auth/auth.module'
import {AppConfig} from './config/schemas/app.config.schema'
import {DatabaseModule} from './database/database.module'
import {RedisModule} from './database/redis.module'
import {ExpandModule} from './helpers/expand.module'
import {TaskAgentModule} from './helpers/task-agent.module'
import {InterceptorModule} from './interceptors/interceptor.module'
import {LicenseModule} from './license/license.module'
import {LicenseService} from './license/license.service'
import {LoggerService} from './logger/logger.service'
import {ContextMiddleware} from './middleware/context.middleware'
import {LoggerMiddleware} from './middleware/logger.middleware'
import {StateMiddleware} from './middleware/state.middleware'
import {DbStateSchedule} from './schedules/dbstate.schedule'
import {FileshareSchedule} from './schedules/fileshare.schedule'
import {RedisStateSchedule} from './schedules/redisstate.schedule'

let modulesImport: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference> = [
    ConfigModule.forRoot({
        isGlobal: true,
        ignoreEnvFile: true,
        load: [
            function (): AppConfig {
                return AppService.config
            },
        ],
    }),
    I18nModule.forRoot({
        fallbackLanguage: 'en',
        loaderOptions: {
            path: path.join(__dirname, '/localisation/'),
            watch: true,
        },
        resolvers: [
            {use: QueryResolver, options: ['lang', 'locale', 'l']},
            new HeaderResolver(['x-custom-lang']),
            AcceptLanguageResolver,
            new CookieResolver(['lang', 'locale', 'l']),
        ],
    }),
    AdminModule,
    AuthModule,
    BanAdminModule,
    BanRegistrationModule,
    BanIpModule,
    BanSubscriberModule,
    BanModule,
    ClearCallCounterModule,
    ContactModule,
    ContractModule,
    CustomerBalanceModule,
    CustomerPhonebookModule,
    CustomerModule,
    CustomerContactModule,
    CustomerSpeedDialModule,
    DatabaseModule,
    DomainModule,
    ExpandModule,
    FileshareModule,
    NCOSLevelModule,
    NCOSPatternModule,
    NCOSModule,
    NCOSSetModule,
    HeaderManipulationModule,
    HeaderManipulationRuleConditionModule,
    HeaderManipulationRuleActionModule,
    HeaderManipulationRuleModule,
    HeaderManipulationSetModule,
    InvoiceTemplateModule,
    NumberModule,
    OtpModule,
    PbxUserModule,
    PbxModule,
    PbxGroupMemberModule,
    PbxGroupModule,
    PeeringModule,
    PeeringGroupServerModule,
    PeeringInboundRuleModule,
    PeeringRuleModule,
    PeeringGroupModule,
    ProductModule,
    RedisModule,
    ResellerPhonebookModule,
    ResellerModule,
    RewriteModule,
    RewriteRuleModule,
    RewriteRuleSetModule,
    SubscriberPhonebookModule,
    SubscriberModule,
    SystemContactModule,
    TemplateModule,
    VoicemailModule,
    LicenseModule,
    PasswordModule,
    PasswordChangeModule,
    ScheduleModule.forRoot(),
    TaskAgentModule,
]
if (process.env.NODE_ENV != 'test') {
    modulesImport = [
        ...modulesImport,
        InterceptorModule,
        JournalModule,
    ]
}

@Global()
@Module({
    controllers: [
        AppController,
    ],
    imports: [
        ...modulesImport,
    ],
    exports: [
        AppService,
        Logger,
        LoggerService,
        LicenseService,
    ],
    providers: [
        AppService,
        DbStateSchedule,
        FileshareSchedule,
        RedisStateSchedule,
        Logger,
        LoggerService,
        LicenseService,
    ],
})
export class AppModule implements NestModule {
    configure(consumer: MiddlewareConsumer): void {
        consumer.apply(ContextMiddleware, LoggerMiddleware, StateMiddleware).forRoutes({
            path: '*',
            method: RequestMethod.ALL,
        })
    }
}
