<?php
namespace App\Services;

use App\Models\Company_user;
use App\Models\Product;
use App\Models\Sale;
use App\Models\Sale_details;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class PosServices
{
	protected RateService $rateService;
	protected SalesService $salesService;

	public function __construct(RateService $rateService, SalesService $salesService)
	{
		$this->rateService = $rateService;
		$this->salesService = $salesService;
	}

	public function getActiveCategories(): Collection
	{
		return DB::table('product_category')
			->select('id', 'category_name')
			->where('is_active', 1)
			->orderBy('category_name')
			->get();
	}

	public function searchProducts(?string $searchValue = null, ?int $categoryId = null, int $limit = 24, ?int $companyId = null): Collection
	{
		$query = Product::query()
			->select([
				'id',
				'product_name',
				'description',
				'sale_price',
				'current_stock',
				'category_id',
				'barcode',
			])
			->where('is_active', 1)
			->where('current_stock', '>', 0)
			->orderBy('product_name');

		if ($companyId !== null && $companyId > 0) {
			$query->where('company_id', $companyId);
		}

		if (! empty($searchValue)) {
			$query->where(function ($subQuery) use ($searchValue): void {
				$subQuery->where('product_name', 'like', "%{$searchValue}%")
					->orWhere('description', 'like', "%{$searchValue}%")
					->orWhere('barcode', 'like', "%{$searchValue}%");
			});
		}

		if (! empty($categoryId)) {
			$query->where('category_id', $categoryId);
		}

		return $query
			->limit($limit)
			->get()
			->map(function (Product $product): array {
				return [
					'id' => $product->id,
					'name' => $product->product_name,
					'description' => $product->description,
					'sale_price' => (float) $product->sale_price,
					'current_stock' => (int) $product->current_stock,
					'category_id' => $product->category_id,
				];
			});
	}

	public function getCompanyUsers(User $authUser, ?string $searchValue = null, int $limit = 120): Collection
	{
		$companyId = Company_user::query()
			->where('user_id', $authUser->id)
			->value('company_id');

		if (empty($companyId)) {
			return collect();
		}

		$query = User::query()
			->select([
				'users.id',
				'users.name',
				'users.people_id',
				'people.last_name',
				'people.phone',
			])
			->join('company_user', 'company_user.user_id', '=', 'users.id')
			->leftJoin('people', 'people.id', '=', 'users.people_id')
			->where('company_user.company_id', $companyId)
			->where('users.is_active', 1)
			->orderBy('users.name');

		if (! empty($searchValue)) {
			$query->where(function ($subQuery) use ($searchValue): void {
				$subQuery->where('users.name', 'like', "%{$searchValue}%")
					->orWhere('people.last_name', 'like', "%{$searchValue}%")
					->orWhere('people.phone', 'like', "%{$searchValue}%");
			});
		}

		return $query->limit($limit)->get()->map(function ($user): array {
			$fullName = trim((string) ($user->name ?? '') . ' ' . (string) ($user->last_name ?? ''));

			return [
				'id' => (int) $user->id,
				'name' => $user->name,
				'last_name' => $user->last_name,
				'full_name' => $fullName !== '' ? $fullName : ('Usuario #' . $user->id),
				'phone' => $user->phone,
				'people_id' => $user->people_id,
			];
		});
	}

	public function createSale(User $authUser, int $targetUserId, array $items): Sale
	{
		$targetUser = User::query()->find($targetUserId);

		if (! $targetUser) {
			throw ValidationException::withMessages([
				'user_id' => 'El usuario seleccionado no existe.',
			]);
		}

		if (empty($targetUser->people_id)) {
			throw ValidationException::withMessages([
				'user_id' => 'El usuario seleccionado no tiene persona asociada para registrar la venta.',
			]);
		}

		$companyId = Company_user::query()
			->where('user_id', $authUser->id)
			->value('company_id');

		if (empty($companyId)) {
			throw ValidationException::withMessages([
				'company' => 'El usuario autenticado no pertenece a una empresa.',
			]);
		}

		$targetBelongsToCompany = Company_user::query()
			->where('company_id', $companyId)
			->where('user_id', $targetUser->id)
			->exists();

		if (! $targetBelongsToCompany) {
			throw ValidationException::withMessages([
				'user_id' => 'El usuario seleccionado no pertenece a tu empresa.',
			]);
		}

		$normalizedItems = collect($items)
			->filter(function ($item): bool {
				return isset($item['product_id'], $item['quantity'])
					&& (int) $item['product_id'] > 0
					&& (int) $item['quantity'] > 0;
			})
			->groupBy(function ($item): int {
				return (int) $item['product_id'];
			})
			->map(function (Collection $group): int {
				return (int) $group->sum(function ($entry): int {
					return (int) $entry['quantity'];
				});
			});

		if ($normalizedItems->isEmpty()) {
			throw ValidationException::withMessages([
				'items' => 'Debes agregar al menos un producto valido a la venta.',
			]);
		}

		$rate = $this->rateService->getActiveRate();
		$usdRate = $rate ? (float) $rate->usd_rate : 1.0;

		return DB::transaction(function () use ($normalizedItems, $targetUser, $companyId, $usdRate): Sale {
			$products = Product::query()
				->whereIn('id', $normalizedItems->keys()->all())
				->where('company_id', $companyId)
				->lockForUpdate()
				->get()
				->keyBy('id');

			if ($products->count() !== $normalizedItems->count()) {
				throw ValidationException::withMessages([
					'items' => 'Algunos productos no existen o ya no estan disponibles.',
				]);
			}

			$detailsPayload = [];
			$baseTotal = 0.0;

			foreach ($normalizedItems as $productId => $quantity) {
				$product = $products->get((int) $productId);

				if (! $product || (int) $product->is_active !== 1) {
					throw ValidationException::withMessages([
						'items' => "El producto #{$productId} no esta activo.",
					]);
				}

				if ((int) $product->current_stock < (int) $quantity) {
					throw ValidationException::withMessages([
						'items' => "Stock insuficiente para {$product->product_name}.",
					]);
				}

				$unitPrice = (float) $product->sale_price;
				$subtotal = round($unitPrice * (int) $quantity, 2);
				$baseTotal += $subtotal;

				$detailsPayload[] = [
					'product' => $product,
					'quantity' => (int) $quantity,
					'unit_price' => $unitPrice,
					'subtotal' => $subtotal,
				];
			}

			$baseTotal = round($baseTotal, 2);
			$totalWithRate = round($baseTotal * $usdRate, 2);
			$saleIdentifier = $this->salesService->getNewSaleIdentifier((int) $companyId);

			$sale = Sale::create([
				'sale_identifier' => $saleIdentifier,
				'people_id' => (int) $targetUser->people_id,
				'user_id' => (int) $targetUser->id,
				'company_id' => (int) $companyId,
				'status' => 'pendiente',
				'total' => $totalWithRate,
				'debt' => $totalWithRate,
				'aux_total' => $baseTotal,
				'aux_debt' => $baseTotal,
				'amount_rate' => $usdRate,
			]);

			foreach ($detailsPayload as $detail) {
				$unitPriceWithRate = round($detail['unit_price'] * $usdRate, 2);
				$subtotalWithRate = round($detail['subtotal'] * $usdRate, 2);

				Sale_details::create([
					'sale_id' => $sale->id,
					'product_id' => $detail['product']->id,
					'membership_user_id' => null,
					'quantity' => $detail['quantity'],
					'unit_price' => $unitPriceWithRate,
					'subtotal' => $subtotalWithRate,
					'aux_unit_price' => $detail['unit_price'],
					'aux_subtotal' => $detail['subtotal'],
				]);
			}

			return $sale->load('saleDetails.product');
		});
	}

}