<?php

namespace App\Services\Hr;

use App\Models\Asset;
use App\Models\FnfSettlement;
use App\Models\Offboarding;
use App\Models\OffboardingChecklistItem;
use App\Models\User;
use App\Notifications\AssetNotReturnedNotification;
use App\Notifications\FnfPendingApprovalNotification;
use App\Notifications\OffboardingChecklistAssignedNotification;
use App\Notifications\OffboardingClearanceCompleteNotification;
use App\Services\HrmsNotificationService;
use Illuminate\Support\Facades\DB;

class OffboardingWorkflowService
{
    public const DEFAULT_CHECKLIST = [
        ['category' => 'manager', 'task_name' => 'Knowledge transfer sign-off', 'description' => 'Manager confirms handover of responsibilities.'],
        ['category' => 'it', 'task_name' => 'Disable system access', 'description' => 'Revoke email, VPN, and application accounts.'],
        ['category' => 'assets', 'task_name' => 'Return assigned assets', 'description' => 'Collect laptop, ID card, and other company assets.'],
        ['category' => 'hr', 'task_name' => 'Exit interview', 'description' => 'Conduct exit interview and document feedback.'],
        ['category' => 'hr', 'task_name' => 'Initiate F&F settlement', 'description' => 'Prepare full and final settlement worksheet.'],
        ['category' => 'finance', 'task_name' => 'Clear advances / loans', 'description' => 'Verify outstanding advances and loan recoveries.'],
        ['category' => 'docs', 'task_name' => 'Relieving letter & experience certificate', 'description' => 'Issue exit documents after clearance.'],
    ];

    public function __construct(
        protected FnfSettlementCalculator $fnfCalculator,
        protected OffboardingAssigneeResolver $assigneeResolver,
        protected HrmsNotificationService $notifier,
        protected EmployeeAccessRevocationService $accessRevocation
    ) {}

    public function initiate(Offboarding $offboarding): Offboarding
    {
        $offboarding = DB::transaction(function () use ($offboarding) {
            $user = User::withoutGlobalScopes()->findOrFail($offboarding->user_id);
            $lwd = $offboarding->last_working_date ?? $offboarding->offboarding_date;

            $offboarding->update([
                'last_working_date' => $lwd,
                'status' => Offboarding::STATUS_CLEARANCE_PENDING,
            ]);

            if ($lwd) {
                $user->update(['last_working_date' => $lwd]);
            }

            $this->seedChecklist($offboarding, $user);
            $this->seedAssetChecklistItems($offboarding, $user);
            $this->fnfCalculator->createDraftFromOffboarding($offboarding->fresh());

            $offboarding = $offboarding->fresh(['checklistItems', 'fnfSettlement', 'user']);
            $this->notifyChecklistAssignees($offboarding);

            return $offboarding;
        });

        $user = User::withoutGlobalScopes()->find($offboarding->user_id);
        if ($user) {
            $this->accessRevocation->deactivateIfLastWorkingDatePassed($user);
        }

        return $offboarding->fresh(['checklistItems', 'fnfSettlement', 'user']) ?? $offboarding;
    }

    public function updateChecklistItem(OffboardingChecklistItem $item, string $status, ?string $notes, int $actorId): OffboardingChecklistItem
    {
        $item->update([
            'status' => $status,
            'notes' => $notes,
            'completed_at' => in_array($status, [
                OffboardingChecklistItem::STATUS_COMPLETED,
                OffboardingChecklistItem::STATUS_WAIVED,
                OffboardingChecklistItem::STATUS_NA,
            ], true) ? now() : null,
            'completed_by' => in_array($status, [
                OffboardingChecklistItem::STATUS_COMPLETED,
                OffboardingChecklistItem::STATUS_WAIVED,
            ], true) ? $actorId : null,
        ]);

        $this->syncOffboardingStatus($item->offboarding->fresh(['checklistItems', 'fnfSettlement', 'user']));

        if ($item->category === 'assets' && in_array($status, [
            OffboardingChecklistItem::STATUS_COMPLETED,
            OffboardingChecklistItem::STATUS_WAIVED,
        ], true)) {
            $this->notifyPendingAssets($item->offboarding->fresh(['user']));
        }

        return $item->fresh();
    }

    public function syncOffboardingStatus(Offboarding $offboarding): void
    {
        if ($offboarding->status === Offboarding::STATUS_CANCELLED) {
            return;
        }

        if ($offboarding->isClearanceComplete()) {
            $wasCleared = $offboarding->status === Offboarding::STATUS_CLEARED;
            $offboarding->update(['status' => Offboarding::STATUS_CLEARED]);

            if (! $wasCleared) {
                $this->notifier->notifyOffboardingStakeholders(
                    (int) $offboarding->company_id,
                    new OffboardingClearanceCompleteNotification($offboarding),
                    [(int) $offboarding->user_id]
                );
                $this->notifyPendingAssets($offboarding);
            }

            $fnf = $offboarding->fnfSettlement;
            if ($fnf && $fnf->status === FnfSettlement::STATUS_DRAFT) {
                $fnf->update(['status' => FnfSettlement::STATUS_PENDING_APPROVAL]);
                $fnf->load('user');
                $this->notifier->notifyFnfApprovers(
                    (int) $offboarding->company_id,
                    new FnfPendingApprovalNotification($fnf),
                    [(int) $offboarding->user_id]
                );
            }

            return;
        }

        if ($offboarding->status === Offboarding::STATUS_CLEARED && ! $offboarding->isClearanceComplete()) {
            $offboarding->update(['status' => Offboarding::STATUS_CLEARANCE_PENDING]);
        }
    }

    protected function notifyPendingAssets(Offboarding $offboarding): void
    {
        if (! $offboarding->user_id || ! $offboarding->company_id) {
            return;
        }

        $pendingCount = Asset::withoutGlobalScopes()
            ->where('company_id', $offboarding->company_id)
            ->where('assigned_to', $offboarding->user_id)
            ->where('status', 'assigned')
            ->count();

        if ($pendingCount <= 0) {
            return;
        }

        $this->notifier->notifyUsersWithAnyPermission(
            (int) $offboarding->company_id,
            ['hr_operations_view', 'assets_view'],
            new AssetNotReturnedNotification($offboarding, $pendingCount),
            [(int) $offboarding->user_id]
        );
    }

    protected function seedChecklist(Offboarding $offboarding, User $exitingEmployee): void
    {
        if ($offboarding->checklistItems()->exists()) {
            return;
        }

        $order = 0;
        foreach (self::DEFAULT_CHECKLIST as $task) {
            OffboardingChecklistItem::create([
                'company_id' => $offboarding->company_id,
                'offboarding_id' => $offboarding->id,
                'category' => $task['category'],
                'task_name' => $task['task_name'],
                'description' => $task['description'],
                'status' => OffboardingChecklistItem::STATUS_PENDING,
                'assigned_to' => $this->assigneeResolver->resolveAssigneeId(
                    (int) $offboarding->company_id,
                    $task['category'],
                    $exitingEmployee
                ),
                'display_order' => $order++,
            ]);
        }
    }

    protected function seedAssetChecklistItems(Offboarding $offboarding, User $exitingEmployee): void
    {
        $assets = Asset::withoutGlobalScopes()
            ->where('company_id', $offboarding->company_id)
            ->where('assigned_to', $exitingEmployee->id)
            ->where('status', 'assigned')
            ->get();

        if ($assets->isEmpty()) {
            return;
        }

        $maxOrder = (int) $offboarding->checklistItems()->max('display_order');
        $assigneeId = $this->assigneeResolver->resolveAssigneeId(
            (int) $offboarding->company_id,
            'assets',
            $exitingEmployee
        );

        foreach ($assets as $asset) {
            $exists = $offboarding->checklistItems()
                ->where('category', 'assets')
                ->where('task_name', 'Return: ' . $asset->name)
                ->exists();

            if ($exists) {
                continue;
            }

            OffboardingChecklistItem::create([
                'company_id' => $offboarding->company_id,
                'offboarding_id' => $offboarding->id,
                'category' => 'assets',
                'task_name' => 'Return: ' . $asset->name,
                'description' => 'Serial: ' . ($asset->serial_number ?: '—'),
                'status' => OffboardingChecklistItem::STATUS_PENDING,
                'assigned_to' => $assigneeId,
                'display_order' => ++$maxOrder,
            ]);
        }
    }

    protected function notifyChecklistAssignees(Offboarding $offboarding): void
    {
        $notified = [(int) $offboarding->user_id];

        foreach ($offboarding->checklistItems as $item) {
            if (! $item->assigned_to || in_array((int) $item->assigned_to, $notified, true)) {
                continue;
            }

            $assignee = User::withoutGlobalScopes()->find($item->assigned_to);
            if (! $assignee) {
                continue;
            }

            $assignee->notify(new OffboardingChecklistAssignedNotification($offboarding, $item));
            $notified[] = (int) $assignee->id;
        }
    }
}
