<?php

namespace App\Providers;

use App\Contracts\VirusScanner;
use App\Events\CompanyCreated;
use App\Listeners\InitializeCompanyDefaults;
use App\Models\Attendance;
use App\Models\WorkDuration;
use App\Services\Attendance\AttendancePolicyResolver;
use App\Services\Attendance\AttendancePunchValidator;
use App\Services\Attendance\WeekoffResolver;
use App\Services\Finance\FinanceNavService;
use App\Services\Security\NullVirusScanner;
use Carbon\Carbon;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->bind(VirusScanner::class, NullVirusScanner::class);
    }

    public function boot(): void
    {
        // MySQL: index key length limit (767/1000 bytes with utf8mb4)
        Schema::defaultStringLength(191);

        $this->configureAuthRateLimiting();

        ResetPassword::createUrlUsing(function (object $user, string $token) {
            return route('password.reset', [
                'token' => $token,
                'email' => $user->email,
                'company' => $user->company?->short_name,
            ]);
        });

        Event::listen(CompanyCreated::class, InitializeCompanyDefaults::class);

        // Apply Super Admin email settings from storage if present
        $path = storage_path('app/superadmin_settings.json');
        if (File::exists($path)) {
            $data = json_decode(File::get($path), true);
            if (is_array($data) && ! empty($data['mail_from_address'])) {
                Config::set('mail.default', $data['mail_mailer'] ?? 'smtp');
                Config::set('mail.from.name', $data['mail_from_name'] ?? config('app.name'));
                Config::set('mail.from.address', $data['mail_from_address']);
                Config::set('mail.mailers.smtp.host', $data['mail_host'] ?? '');
                Config::set('mail.mailers.smtp.port', (int) ($data['mail_port'] ?? 587));
                Config::set('mail.mailers.smtp.username', $data['mail_username'] ?? '');
                Config::set('mail.mailers.smtp.password', $data['mail_password'] ?? '');
                Config::set('mail.mailers.smtp.encryption', $data['mail_encryption'] ?? 'tls');
            }
            if (is_array($data)) {
                Config::set('lead.fast2sms.enabled', ($data['fast2sms_enabled'] ?? 'no') === 'yes');
                if (! empty($data['fast2sms_api_key'])) {
                    Config::set('lead.fast2sms.api_key', $data['fast2sms_api_key']);
                }
                if (array_key_exists('fast2sms_entity_id', $data)) {
                    Config::set('lead.fast2sms.entity_id', (string) $data['fast2sms_entity_id']);
                }
                if (array_key_exists('fast2sms_sender_id', $data)) {
                    Config::set('lead.fast2sms.sender_id', (string) $data['fast2sms_sender_id']);
                }
                if (array_key_exists('fast2sms_route', $data)) {
                    Config::set('lead.fast2sms.route', (string) $data['fast2sms_route']);
                }
                if (array_key_exists('fast2sms_otp_template', $data)) {
                    Config::set('lead.fast2sms.otp_template', (string) $data['fast2sms_otp_template']);
                }
                if (array_key_exists('fast2sms_ack_template', $data)) {
                    Config::set('lead.fast2sms.ack_template', (string) $data['fast2sms_ack_template']);
                }
                if (array_key_exists('fast2sms_followup_template', $data)) {
                    Config::set('lead.fast2sms.followup_template', (string) $data['fast2sms_followup_template']);
                }
                Config::set('lead.whatsapp.enabled', ($data['kiyowap_enabled'] ?? 'no') === 'yes');
                if (array_key_exists('kiyowap_api_base_url', $data)) {
                    Config::set('lead.whatsapp.api_base_url', (string) $data['kiyowap_api_base_url']);
                }
                if (! empty($data['kiyowap_access_token'])) {
                    Config::set('lead.whatsapp.access_token', (string) $data['kiyowap_access_token']);
                }
                if (array_key_exists('kiyowap_vendor_uid', $data)) {
                    Config::set('lead.whatsapp.vendor_uid', (string) $data['kiyowap_vendor_uid']);
                }
                if (array_key_exists('kiyowap_phone_number_id', $data)) {
                    Config::set('lead.whatsapp.phone_number_id', (string) $data['kiyowap_phone_number_id']);
                }
                if (array_key_exists('kiyowap_template_new_lead', $data)) {
                    Config::set('lead.whatsapp.template_new_lead', (string) $data['kiyowap_template_new_lead']);
                }
                if (array_key_exists('kiyowap_template_followup', $data)) {
                    Config::set('lead.whatsapp.template_followup', (string) $data['kiyowap_template_followup']);
                }
            }
        }

        // Tenant sidebar: pass allowed modules from subscription plan (empty = all allowed)
        View::composer('layouts.partials.sidebar', function ($view) {
            $allowedModules = null;
            $financeHomeUrl = null;
            if (Auth::check()) {
                $financeHomeUrl = app(FinanceNavService::class)->homeUrl(Auth::user());
                if (! (Auth::user()->is_superadmin ?? false) && Auth::user()->company_id) {
                    $company = Auth::user()->company;
                    if ($company && $company->subscriptionPlan && is_array($company->subscriptionPlan->modules) && count($company->subscriptionPlan->modules) > 0) {
                        $allowedModules = $company->subscriptionPlan->modules;
                    }
                }
            }
            $view->with([
                'allowedModules' => $allowedModules,
                'financeHomeUrl' => $financeHomeUrl,
            ]);
        });

        // Workforce staff only: clock + check-in/out in top bar (not company admin / super admin)
        View::composer('layouts.app', function ($view) {
            $view->with($this->selfAttendancePunchViewData());
        });

        // Punch forms are often included from child @section content (e.g. dashboard/self)
        // before layouts.app is composed — attach the same policy/office data to the partial.
        View::composer('attendance.partials.punch-forms', function ($view) {
            $data = $this->selfAttendancePunchViewData();
            $view->with([
                'todayHasOpenSession' => $data['todayHasOpenSession'],
                'todayIsWeekoff' => $data['todayIsWeekoff'],
                'todayAttendance' => $data['todayAttendance'],
                'attendancePolicy' => $data['attendancePolicy'],
                'attendanceOffices' => $data['attendanceOffices'],
            ]);
        });

        View::composer(['finance.top-submenu', 'finance.partials.nav'], function ($view) {
            if (! Auth::check()) {
                return;
            }

            $nav = app(FinanceNavService::class);
            $user = Auth::user();

            $view->with([
                'financePrimaryTabs' => $nav->primaryTabs($user),
                'financeSetupTabs' => $nav->setupTabs($user),
                'financeZoneLabel' => $nav->zoneLabel($user),
                'financeHomeUrl' => $nav->homeUrl($user),
                'financeBreadcrumbs' => $nav->breadcrumbs($user),
            ]);
        });

    }

    /**
     * Shared self-attendance punch payload for topbar + punch-forms (request-memoized).
     *
     * @return array{
     *     showSelfAttendanceTopbar: bool,
     *     todayHasOpenSession: bool,
     *     todayIsWeekoff: bool,
     *     todayAttendance: ?Attendance,
     *     attendancePolicy: mixed,
     *     attendanceOffices: array<int, array<string, mixed>>
     * }
     */
    protected function selfAttendancePunchViewData(): array
    {
        $key = 'hrms.selfAttendancePunchViewData';
        if ($this->app->bound($key)) {
            return $this->app->make($key);
        }

        $showSelfAttendanceTopbar = false;
        $todayHasOpenSession = false;
        $todayIsWeekoff = false;
        $todayAttendance = null;
        $attendancePolicy = null;
        $attendanceOffices = [];

        if (Auth::check()) {
            $user = Auth::user();
            $showSelfAttendanceTopbar = $user->isWorkforceMember();
            if ($showSelfAttendanceTopbar) {
                $today = Carbon::today()->toDateString();
                $todayDate = Carbon::today();
                $todayIsWeekoff = app(WeekoffResolver::class)->isWeekoff(
                    (int) $user->company_id,
                    (int) $user->id,
                    $todayDate
                );
                $attendancePolicy = app(AttendancePolicyResolver::class)->resolveForUser($user);
                $attendanceOffices = app(AttendancePunchValidator::class)
                    ->activeGeofencedOffices((int) $user->company_id)
                    ->map(fn ($office) => [
                        'id' => $office->id,
                        'name' => $office->name,
                        'latitude' => (float) $office->latitude,
                        'longitude' => (float) $office->longitude,
                        'radius_meters' => (int) ($office->radius_meters ?: $attendancePolicy->defaultRadiusMeters),
                        'is_assigned' => (int) $office->id === (int) $user->location_id,
                    ])
                    ->values()
                    ->all();
                $todayAttendance = Attendance::with('clockInOffice')->where('company_id', $user->company_id)
                    ->where('user_id', $user->id)
                    ->where('date', $today)
                    ->first();
                if ($todayAttendance) {
                    $todayHasOpenSession = WorkDuration::query()
                        ->where('company_id', $user->company_id)
                        ->where('attendance_id', $todayAttendance->id)
                        ->whereNull('end_time')
                        ->exists();
                }
            }
        }

        $data = [
            'showSelfAttendanceTopbar' => $showSelfAttendanceTopbar,
            'todayHasOpenSession' => $todayHasOpenSession,
            'todayIsWeekoff' => $todayIsWeekoff,
            'todayAttendance' => $todayAttendance,
            'attendancePolicy' => $attendancePolicy,
            'attendanceOffices' => $attendanceOffices,
        ];

        $this->app->instance($key, $data);

        return $data;
    }

    protected function configureAuthRateLimiting(): void
    {
        RateLimiter::for('auth-login', function (Request $request) {
            $email = strtolower((string) $request->input('email', ''));

            return [
                Limit::perMinute(10)->by($request->ip()),
                Limit::perMinute(5)->by($email !== '' ? $email : $request->ip()),
            ];
        });

        RateLimiter::for('auth-two-factor', function (Request $request) {
            return Limit::perMinute(10)->by($request->ip());
        });

        RateLimiter::for('auth-two-factor-resend', function (Request $request) {
            $key = (string) ($request->input('challenge_token')
                ?? $request->session()->get('login.id')
                ?? $request->ip());

            return Limit::perMinute(3)->by($key);
        });

        RateLimiter::for('face-login', function (Request $request) {
            return Limit::perMinute(10)->by($request->ip());
        });

        RateLimiter::for('zkteco-webhook', function (Request $request) {
            $user = (string) $request->getUser();

            return [
                Limit::perMinute(60)->by($request->ip()),
                Limit::perMinute(120)->by($user !== '' ? 'zkteco:'.$user : $request->ip()),
            ];
        });

        RateLimiter::for('api', function (Request $request) {
            $key = $request->user()?->id ?: $request->ip();

            return Limit::perMinute(60)->by($key);
        });

        RateLimiter::for('face-attendance', function (Request $request) {
            $key = $request->user()?->id ?: $request->ip();

            return Limit::perMinute(20)->by($key);
        });
    }
}
