<?php

namespace App\Providers;

use App\Contracts\NationalIdentityProvider;
use App\Models\Collateral;
use App\Models\CollectionFollowUp;
use App\Models\Customer;
use App\Models\CustomerAppointment;
use App\Models\DailyClosing;
use App\Models\Expense;
use App\Models\ExpenseReminder;
use App\Models\FinancialAccount;
use App\Models\FinancialAccountTransaction;
use App\Models\FixedAsset;
use App\Models\Loan;
use App\Models\LoanDisbursement;
use App\Models\LoanEarlySettlement;
use App\Models\LoanPayment;
use App\Models\LoanPenaltyCharge;
use App\Models\LoanProduct;
use App\Models\LoanRestructuring;
use App\Models\LoanTopUp;
use App\Models\LoanWriteOff;
use App\Models\ManagementAlert;
use App\Models\PayrollPeriod;
use App\Models\RecurringExpense;
use App\Models\ReminderNotificationLog;
use App\Models\RemotePaymentSubmission;
use App\Models\Scopes\BranchScope;
use App\Models\Task;
use App\Models\User;
use App\Observers\DailyClosingObserver;
use App\Policies\CollateralPolicy;
use App\Policies\CustomerAppointmentPolicy;
use App\Policies\CustomerPolicy;
use App\Policies\DailyClosingPolicy;
use App\Policies\ExpensePolicy;
use App\Policies\FinancialAccountPolicy;
use App\Policies\FixedAssetPolicy;
use App\Policies\LoanDisbursementPolicy;
use App\Policies\LoanEarlySettlementPolicy;
use App\Policies\LoanPaymentPolicy;
use App\Policies\LoanPolicy;
use App\Policies\LoanProductPolicy;
use App\Policies\LoanRestructuringPolicy;
use App\Policies\LoanTopUpPolicy;
use App\Policies\LoanWriteOffPolicy;
use App\Policies\ManagementAlertPolicy;
use App\Policies\PayrollPeriodPolicy;
use App\Policies\RecurringExpensePolicy;
use App\Policies\ReminderNotificationLogPolicy;
use App\Policies\RemotePaymentSubmissionPolicy;
use App\Policies\TaskPolicy;
use App\Services\BranchContext;
use App\Services\LocalizationService;
use App\Services\NationalIdentity\NidaNationalIdentityProvider;
use Illuminate\Database\Events\QueryExecuted;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(NationalIdentityProvider::class, NidaNationalIdentityProvider::class);
        $this->app->singleton(BranchContext::class);
        $this->app->singleton(LocalizationService::class);
    }

    public function boot(): void
    {
        Gate::before(function (User $user, string $ability): ?bool {
            return $user->hasRole('super-administrator') ? true : null;
        });

        Gate::policy(Customer::class, CustomerPolicy::class);
        Gate::policy(LoanProduct::class, LoanProductPolicy::class);
        Gate::policy(Loan::class, LoanPolicy::class);
        Gate::policy(Collateral::class, CollateralPolicy::class);
        Gate::policy(LoanDisbursement::class, LoanDisbursementPolicy::class);
        Gate::policy(LoanPayment::class, LoanPaymentPolicy::class);
        Gate::policy(RemotePaymentSubmission::class, RemotePaymentSubmissionPolicy::class);
        Gate::policy(LoanTopUp::class, LoanTopUpPolicy::class);
        Gate::policy(LoanEarlySettlement::class, LoanEarlySettlementPolicy::class);
        Gate::policy(LoanRestructuring::class, LoanRestructuringPolicy::class);
        Gate::policy(LoanWriteOff::class, LoanWriteOffPolicy::class);
        Gate::policy(FinancialAccount::class, FinancialAccountPolicy::class);
        Gate::policy(Expense::class, ExpensePolicy::class);
        Gate::policy(RecurringExpense::class, RecurringExpensePolicy::class);
        Gate::policy(PayrollPeriod::class, PayrollPeriodPolicy::class);
        Gate::policy(DailyClosing::class, DailyClosingPolicy::class);
        Gate::policy(FixedAsset::class, FixedAssetPolicy::class);
        Gate::policy(Task::class, TaskPolicy::class);
        Gate::policy(CustomerAppointment::class, CustomerAppointmentPolicy::class);
        Gate::policy(ManagementAlert::class, ManagementAlertPolicy::class);
        Gate::policy(ReminderNotificationLog::class, ReminderNotificationLogPolicy::class);

        DailyClosing::observe(DailyClosingObserver::class);

        $slowQueryThreshold = (int) config('database_performance.slow_query_ms', 0);
        if ($slowQueryThreshold > 0) {
            DB::listen(function (QueryExecuted $query) use ($slowQueryThreshold): void {
                if ($query->time < $slowQueryThreshold) {
                    return;
                }

                Log::warning('database.slow_query', [
                    'connection' => $query->connectionName,
                    'duration_ms' => round($query->time, 2),
                    'sql' => $query->sql,
                ]);
            });
        }

        Loan::addGlobalScope(new BranchScope());
        LoanPayment::addGlobalScope(new BranchScope());
        LoanPenaltyCharge::addGlobalScope(new BranchScope());
        CollectionFollowUp::addGlobalScope(new BranchScope());
        Collateral::addGlobalScope(new BranchScope());
        FinancialAccount::addGlobalScope(new BranchScope());
        FinancialAccountTransaction::addGlobalScope(new BranchScope());
        Expense::addGlobalScope(new BranchScope());
        RecurringExpense::addGlobalScope(new BranchScope());
        ExpenseReminder::addGlobalScope(new BranchScope());
    }
}
