<?php

namespace Modules\Operations\Services;

use App\Models\ActivityLogModel;
use App\Models\OperationsApprovalRequestModel;
use Modules\Auth\Models\UserModel;

class OperationsApprovalRequestService
{
    protected OperationsApprovalRequestModel $requests;
    protected UserModel $users;
    protected ActivityLogModel $activity;

    public function __construct()
    {
        $this->requests = new OperationsApprovalRequestModel();
        $this->users    = new UserModel();
        $this->activity = new ActivityLogModel();
    }

    public function create(array $input, int $requesterUserId): array
    {
        $title = trim((string) ($input['title'] ?? ''));
        if ($title === '') {
            return ['success' => false, 'errors' => ['title' => 'Title is required.']];
        }

        $type = (string) ($input['request_type'] ?? 'general');
        if (! in_array($type, OperationsApprovalRequestModel::TYPES, true)) {
            $type = 'general';
        }

        $approverId = (int) ($input['approver_user_id'] ?? 0);
        if ($approverId <= 0) {
            return ['success' => false, 'errors' => ['approver_user_id' => 'Select who should approve this request.']];
        }

        if ($approverId === $requesterUserId) {
            return ['success' => false, 'errors' => ['approver_user_id' => 'You cannot send a request to yourself.']];
        }

        $approver = $this->users->find($approverId);
        if (! $approver || ($approver->status ?? '') !== 'active') {
            return ['success' => false, 'errors' => ['approver_user_id' => 'Select an active user as approver.']];
        }

        $amountRaw = trim((string) ($input['amount'] ?? ''));
        $amount = null;
        if ($amountRaw !== '') {
            if (! is_numeric($amountRaw) || (float) $amountRaw < 0) {
                return ['success' => false, 'errors' => ['amount' => 'Enter a valid amount.']];
            }
            $amount = round((float) $amountRaw, 2);
        }

        $data = [
            'number'            => $this->requests->nextNumber(),
            'request_type'      => $type,
            'title'             => $title,
            'description'       => trim((string) ($input['description'] ?? '')) ?: null,
            'amount'            => $amount,
            'requester_user_id' => $requesterUserId,
            'approver_user_id'  => $approverId,
            'status'            => 'pending',
            'created_by'        => $requesterUserId,
            'updated_by'        => $requesterUserId,
        ];

        if (! $this->requests->insert($data, true)) {
            return ['success' => false, 'errors' => $this->requests->errors()];
        }

        $id = (int) $this->requests->getInsertID();
        $this->activity->log(
            'operations_approvals',
            'created',
            $requesterUserId,
            "Sent approval request #{$id} ({$data['number']}) to user #{$approverId}",
            (string) $id
        );

        return ['success' => true, 'id' => $id];
    }

    public function approve(int $id, int $actingUserId): array
    {
        $request = $this->requests->find($id);
        if (! $request) {
            return ['success' => false, 'error' => 'Request not found.'];
        }

        if ((int) $request['approver_user_id'] !== $actingUserId) {
            return ['success' => false, 'error' => 'Only the assigned approver can approve this request.'];
        }

        if ($request['status'] !== 'pending') {
            return ['success' => false, 'error' => 'Only pending requests can be approved.'];
        }

        $this->requests->update($id, [
            'status'           => 'approved',
            'rejection_reason' => null,
            'decided_at'       => date('Y-m-d H:i:s'),
            'updated_by'       => $actingUserId,
        ]);
        $this->activity->log('operations_approvals', 'approved', $actingUserId, "Approved request #{$id}", (string) $id);

        return ['success' => true];
    }

    public function reject(int $id, string $reason, int $actingUserId): array
    {
        $request = $this->requests->find($id);
        if (! $request) {
            return ['success' => false, 'error' => 'Request not found.'];
        }

        if ((int) $request['approver_user_id'] !== $actingUserId) {
            return ['success' => false, 'error' => 'Only the assigned approver can reject this request.'];
        }

        if ($request['status'] !== 'pending') {
            return ['success' => false, 'error' => 'Only pending requests can be rejected.'];
        }

        $this->requests->update($id, [
            'status'           => 'rejected',
            'rejection_reason' => trim($reason) ?: null,
            'decided_at'       => date('Y-m-d H:i:s'),
            'updated_by'       => $actingUserId,
        ]);
        $this->activity->log('operations_approvals', 'rejected', $actingUserId, "Rejected request #{$id}", (string) $id);

        return ['success' => true];
    }

    public function cancel(int $id, int $actingUserId): array
    {
        $request = $this->requests->find($id);
        if (! $request) {
            return ['success' => false, 'error' => 'Request not found.'];
        }

        if ((int) $request['requester_user_id'] !== $actingUserId) {
            return ['success' => false, 'error' => 'Only the requester can cancel this request.'];
        }

        if ($request['status'] !== 'pending') {
            return ['success' => false, 'error' => 'Only pending requests can be cancelled.'];
        }

        $this->requests->update($id, [
            'status'     => 'cancelled',
            'updated_by' => $actingUserId,
        ]);
        $this->activity->log('operations_approvals', 'cancelled', $actingUserId, "Cancelled request #{$id}", (string) $id);

        return ['success' => true];
    }

    /**
     * @return list<array{id:int,label:string}>
     */
    public function activeApproverOptions(?int $excludeUserId = null): array
    {
        $builder = $this->users->where('status', 'active')->orderBy('first_name', 'ASC')->orderBy('last_name', 'ASC');
        if ($excludeUserId) {
            $builder->where('id !=', $excludeUserId);
        }

        $options = [];
        foreach ($builder->findAll() as $user) {
            $options[] = [
                'id'    => (int) $user->id,
                'label' => trim($user->first_name . ' ' . $user->last_name) . ' (' . $user->username . ')',
            ];
        }

        return $options;
    }
}
