<?php

namespace App\Services;

use App\Enums\CustomerStatus;
use App\Models\Customer;
use App\Models\User;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class CustomerService
{
    public function __construct(private NomineeService $nominees) {}

    /**
     * @param  array{
     *     first_name: string,
     *     last_name: string,
     *     email: string|null,
     *     phone: string,
     *     cnic: string|null,
     *     status: CustomerStatus,
     *     address: string|null,
     *     agent_id: int|null,
     *     nominee?: array{
     *         full_name: string,
     *         cnic: string,
     *         phone: string|null,
     *         email: string|null
     *     }|null
     * }  $data
     * @param  list<UploadedFile>  $nomineeDocuments
     */
    public function create(User $actor, array $data, array $nomineeDocuments = []): Customer
    {
        return DB::transaction(function () use ($actor, $data, $nomineeDocuments): Customer {
            $nominee = $data['nominee'] ?? null;
            unset($data['nominee']);

            $customer = Customer::query()->create([
                ...$data,
                'created_by' => $actor->id,
            ]);

            if ($nominee !== null) {
                $this->nominees->sync($actor, $customer, $nominee, $nomineeDocuments);
            }

            $this->assertNomineeRequirements($customer->refresh());

            return $customer;
        });
    }

    /**
     * @param  array{
     *     first_name?: string,
     *     last_name?: string,
     *     email?: string|null,
     *     phone?: string,
     *     cnic?: string|null,
     *     status?: CustomerStatus,
     *     address?: string|null,
     *     agent_id?: int|null,
     *     nominee?: array{
     *         full_name: string,
     *         cnic: string,
     *         phone: string|null,
     *         email: string|null
     *     }
     * }  $data
     * @param  list<UploadedFile>  $nomineeDocuments
     */
    public function update(Customer $customer, User $actor, array $data, array $nomineeDocuments = []): Customer
    {
        return DB::transaction(function () use ($customer, $actor, $data, $nomineeDocuments): Customer {
            $nominee = $data['nominee'] ?? null;
            unset($data['nominee']);

            if ($data !== []) {
                $customer->update($data);
            }

            if ($nominee !== null) {
                $this->nominees->sync($actor, $customer, $nominee, $nomineeDocuments);
            } elseif ($nomineeDocuments !== []) {
                throw ValidationException::withMessages([
                    'nominee' => 'Save nominee details before uploading CNIC images.',
                ]);
            }

            $this->assertNomineeRequirements($customer->refresh());

            return $customer;
        });
    }

    public function delete(Customer $customer): void
    {
        $this->nominees->deleteFilesFor($customer);
        $customer->delete();
    }

    private function assertNomineeRequirements(Customer $customer): void
    {
        $customer->loadMissing('nominee.documents');

        if ($customer->nominee === null && ! $customer->status->requiresCompleteProfile()) {
            return;
        }

        if (! $customer->hasCompleteNominee()) {
            throw ValidationException::withMessages([
                'nominee_documents' => 'Nominee full name, CNIC, and two CNIC images are required.',
            ]);
        }
    }
}
