<?php

namespace App\Modules\HR\Contracts\Services;

use App\Modules\HR\Contracts\DTOs\ContractWriteData;
use App\Modules\HR\Contracts\Models\EmploymentContract;
use App\Modules\HR\Contracts\Repositories\EmploymentContractRepository;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;

class EmploymentContractService
{
    public function __construct(
        private readonly EmploymentContractRepository $repository,
    ) {}

    public function store(ContractWriteData $data, ?UploadedFile $attachment, int $userId): EmploymentContract
    {
        $tenantId = $this->tenantId();
        $path = $attachment !== null
            ? $attachment->store("tenants/{$tenantId}/contracts", 'local')
            : null;

        return $this->repository->create(array_merge($data->toArray(), [
            'tenant_id' => $tenantId,
            'attachment_path' => $path,
            'created_by' => $userId,
        ]));
    }

    public function update(
        EmploymentContract $contract,
        ContractWriteData $data,
        ?UploadedFile $attachment,
    ): EmploymentContract {
        $payload = $data->toArray();
        if ($attachment !== null) {
            $tenantId = (int) $contract->tenant_id;
            if ($contract->attachment_path) {
                Storage::disk('local')->delete($contract->attachment_path);
            }
            $payload['attachment_path'] = $attachment->store("tenants/{$tenantId}/contracts", 'local');
        }

        return $this->repository->update($contract, $payload);
    }

    public function deactivate(EmploymentContract $contract): void
    {
        if (! $contract->is_active) {
            return;
        }
        $this->repository->update($contract, [
            'is_active' => false,
            'status' => 'terminated',
            'terminated_at' => now(),
        ]);
    }

    public function createAmendment(EmploymentContract $contract, int $userId): EmploymentContract
    {
        return DB::transaction(function () use ($contract, $userId): EmploymentContract {
            $copy = $this->copyContract($contract, [
                'version' => $contract->version + 1,
                'parent_contract_id' => $contract->id,
                'status' => 'draft',
                'created_by' => $userId,
            ]);
            $this->repository->update($contract, ['is_active' => false, 'status' => 'expired']);

            return $copy;
        });
    }

    public function renew(EmploymentContract $contract, int $userId): EmploymentContract
    {
        return DB::transaction(function () use ($contract, $userId): EmploymentContract {
            $renewed = $this->copyContract($contract, [
                'version' => 1,
                'renewed_from_id' => $contract->id,
                'renewal_date' => now()->toDateString(),
                'status' => 'draft',
                'e_sign_status' => 'not_required',
                'created_by' => $userId,
            ]);
            $this->repository->update($contract, ['status' => 'renewed', 'is_active' => false]);

            return $renewed;
        });
    }

    public function terminate(EmploymentContract $contract, ?string $reason): EmploymentContract
    {
        return $this->repository->update($contract, [
            'status' => 'terminated',
            'terminated_at' => now(),
            'termination_reason' => $reason,
            'is_active' => false,
        ]);
    }

    /** @param array<string, mixed> $overrides */
    private function copyContract(EmploymentContract $contract, array $overrides): EmploymentContract
    {
        $attributes = $contract->only([
            'tenant_id', 'employee_id', 'template_id', 'contract_type', 'start_date',
            'end_date', 'attachment_path', 'notes', 'e_sign_status',
        ]);

        return $this->repository->create(array_merge($attributes, $overrides));
    }

    private function tenantId(): int
    {
        $id = auth()->user()?->tenant_id;
        if ($id === null) {
            abort(403);
        }

        return (int) $id;
    }
}
