<?php

namespace Modules\Inventory\Services;

use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Modules\Accounts\Enums\AccountTypeEnum;
use Modules\Accounts\Enums\AcctVoucherStatusEnum;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Inventory\DTO\InventorySettingFilter;
use Modules\Inventory\Models\Supplier;
use Modules\Inventory\Repositories\SupplierRepository;
use Modules\Accounts\Repositories\AccountRepository;

class SupplierService
{
    public function __construct(
        protected SupplierRepository $supplierRepo,
        protected AccountRepository $accountRepo,
    ) {}


    public function list(InventorySettingFilter $filter): LengthAwarePaginator
    {
        $columns = [
            'id',
            'name',
            'phone',
            'address',
            'account_id',
            'status'
        ];

        $relations = ['account:id,account_name,account_code'];

        return $this->supplierRepo->all(
            filter: $filter,
            columns: $columns,
            relations: $relations
        );
    }

    public function lookUpList(InventorySettingFilter $filter): LengthAwarePaginator
    {
        $columns = [
            'id',
            'name',
            'phone',
        ];

        return $this->supplierRepo->all(
            filter: $filter,
            columns: $columns
        );
    }

    public function show($id)
    {
        return $this->supplierRepo->find($id);
    }

    public function store(array $data)
    {
        return DB::transaction(function () use ($data) {
            return $this->supplierRepo->create($data);
        });
    }

    public function update(Supplier $supplier, array $data)
    {
        return DB::transaction(function () use ($supplier, $data) {
            $newAccountId = $data['account_id'] ?? $supplier->account_id;

            if ($supplier->account_id !== $newAccountId) {
                $this->ensureAccountIsNotUsed($supplier->account_id);
                $this->validateAccount($newAccountId);
            }

            $data['account_id'] = $newAccountId;

            return $this->supplierRepo->update($supplier, $data);
        });
    }

    protected function ensureAccountIsNotUsed(int $accountId): void
    {
        if ($this->accountHasTransactions($accountId)) {
            ApiValidationException::errors([
                'account_id' => ['This account already has transactions and cannot be modified.']
            ]);
        }
    }

    protected function accountHasTransactions(int $accountId): bool
    {
        return DB::table('account_voucher_lines as avl')
            ->join('account_vouchers as av', 'av.id', '=', 'avl.voucher_id')
            ->where('avl.account_id', $accountId)
            ->where('av.status', AcctVoucherStatusEnum::POSTED->value)
            ->exists();
    }

    protected function validateAccount(int $accountId): void
    {
        $account = $this->accountRepo->find(
            $accountId,
            ['id', 'account_type', 'is_group']
        );

        if ($account->is_group || $account->account_type !== AccountTypeEnum::LIABILITY->value) {
            ApiValidationException::errors([
                'account_id' => ['Invalid liability account selected.']
            ]);
        }
    }

    public function updateStatus(Supplier $supplier, array $status)
    {
        return DB::transaction(function () use ($supplier, $status) {
            return $this->supplierRepo->updateStatus($supplier, $status);
        });
    }

    public function delete(Supplier $supplier): void
    {
        DB::transaction(function () use ($supplier) {
            $this->ensureAccountIsNotUsed($supplier->account_id);

            $supplier->delete();
        });
    }
}
