<?php

namespace Modules\Inventory\Services;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Modules\Inventory\DTO\ProductListFilter;
use Modules\Inventory\Models\Category;
use Modules\Inventory\Models\Product;
use Modules\Accounts\Repositories\AccountRepository;
use Modules\Accounts\Services\AccountLinkedService;
use Modules\Accounts\Services\AccountService;
use Modules\Inventory\Enums\CategoryTypeEnum;
use Modules\Inventory\Models\Traits\GeneratesProductSku;
use Modules\Inventory\Repositories\CategoryRepository;
use Modules\Inventory\Repositories\ProductRepository;

class ProductService
{
    use GeneratesProductSku;

    public function __construct(
        protected AccountService $accountService,
        protected AccountLinkedService $accountLinkedService,
        protected ProductRepository $productRepo,
        protected AccountRepository $accountRepo,
        protected CategoryRepository $categoryRepo,
    ){ }

    public function list(ProductListFilter $filter): LengthAwarePaginator
    {
        $columns = [
            'id',
            'name',
            'sku',
            'vat_price_type',
            'vat_type',
            'inventory_department_id',
            'category_id',
            'base_unit_id',
            'default_selling_price',
            'has_unit_conversion',
            'status'
        ];

        return $this->productRepo->getProducts(
            filter: $filter,
            columns: $columns,
            relations: [
                'inventoryDepartment:id,name',
                'category:id,name',
                'baseUnit:id,name',
            ]
        );
    }
    public function productLookupList(ProductListFilter $filter): LengthAwarePaginator
    {
        $columns = [
            'id',
            'name',
            'sku',
            'vat_price_type',
            'vat_type',
            'barcode',
            'inventory_department_id',
            'category_id',
            'base_unit_id',
            'default_selling_price',
            'mrp',
            'status'
        ];

        return $this->productRepo->getProducts(
            filter: $filter,
            columns: $columns,
            relations: [
                'unitConversions'
            ]
        );
    }

    public function show(int $id)
    {
        return $this->productRepo->find(
            $id,
            relations: [
                'inventoryDepartment:id,name',
                'category:id,name,category_type',
                'baseUnit:id,name',
                'unitConversions.fromUnit:id,name',
                'unitConversions.toUnit:id,name',
                'inventoryAccount:id,account_code,account_name',
                'cogsAccount:id,account_code,account_name',
                'salesAccount:id,account_code,account_name',
                'expenseAccount:id,account_code,account_name',
                'assetAccount:id,account_code,account_name',
            ]
        );
    }

    private function initializeStock(Product $product): void
    {
        $product->stock()->create([
            'inventory_department_id' => $product->inventory_department_id,
            'current_qty' => 0,
            'reserved_qty' => 0,
            'damaged_qty' => 0,
            'status' => $product->status,
        ]);
    }

    public function store(array $data)
    {
        return DB::transaction(function () use ($data) {
            $category = $this->categoryRepo->find($data['category_id']);
            $data = $this->applyCategoryDefaults($data, $category);

            if (blank($data['sku'] ?? null)) {
                $data['sku'] = $this->generateSku($data['product_type']);
            }

            $accountIds = match ($category->category_type) {
                CategoryTypeEnum::ASSET->value => [
                    'asset_account_id' => $category->asset_account_id,
                    'accum_dep_account_id' => $category->accum_dep_account_id,
                    'dep_expense_account_id' => $category->dep_expense_account_id,
                ],

                default => $this->accountService->createProductAccounts(
                    productName: $data['name'],
                    sku: $data['sku'],
                    categoryType: $category->category_type
                ),
            };

            // $product = $this->productRepo->find(2);
            $product = $this->productRepo->create([
                ...$data,
                ...$accountIds,
            ]);

            $this->initializeStock($product);

            DB::afterCommit(function () {
                $this->accountService->clearTreeCache();
            });

            return $product;
        });
    }

    private function applyCategoryDefaults(array $data, Category $category): array
    {
        return [
            ...$data,
            'inventory_department_id' => $category->inventory_department_id,
            'has_stock'  => $category->has_stock,
            'has_batch'  => $category->has_batch,
            'has_expiry' => $category->has_expiry,
        ];
    }

    public function update(Product $product, array $data)
    {
        return DB::transaction(function () use ($product, $data) {
            return $this->productRepo->update($product, $data);
        });
    }

    public function updateStatus(Product $product, array $status)
    {
        return DB::transaction(function () use ($product, $status) {
            return $this->productRepo->updateStatus($product, $status);
        });
    }

    public function previewAccounts(array $data): array
    {
        $category = $this->categoryRepo->findById(
            $data['category_id'],
            [
                'id',
                'category_type',
                'asset_account_id',
                'accum_dep_account_id',
                'dep_expense_account_id',
            ],
            relations: [
                'assetAccount:id,account_code,account_name',
                'accumulatedDepAccount:id,account_code,account_name',
                'depExpenseAccount:id,account_code,account_name',
            ]
        );

        if (blank($data['sku'] ?? null)) {
            $data['sku'] = $this->previewSku($data['product_type']);
        }

        return [
            'sku' => $data['sku'],
            'accounts' => $this->accountLinkedService->previewProductAccounts(
                productName: $data['name'],
                sku: $data['sku'],
                category: $category
            ),
            'note' => 'Preview only. Final SKU and account codes are generated on save.',
        ];
    }
}
