<?php

namespace App\Services\Admin\Manage;

use App\Enums\PriceType;
use App\Enums\ProductStockQuality;
use App\Enums\Role;
use App\Models\ProductVariant;
use App\Models\Restock;
use App\Models\RestockItem;
use App\Services\Concerns\HasStockAdjustment;
use App\Services\Concerns\RegistersMedia;
use App\Services\NotificationService;
use App\Services\S3PresignedService;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

class RestockService
{
    use HasStockAdjustment, RegistersMedia;

    public function __construct(
        private S3PresignedService $s3Service,
    ) {}

    public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', ?int $highlight = null): LengthAwarePaginator
    {
        $itemsCountQuery = '(SELECT COUNT(*) FROM restock_items WHERE restock_items.restock_id = restocks.id AND restock_items.deleted_at IS NULL)';
        $totalQtyQuery = '(SELECT IFNULL(SUM(quantity), 0) FROM restock_items WHERE restock_items.restock_id = restocks.id AND restock_items.deleted_at IS NULL)';
        $productNamesQuery = '(SELECT GROUP_CONCAT(DISTINCT p.name ORDER BY p.name SEPARATOR \', \') FROM restock_items ri JOIN product_variants pv ON pv.id = ri.product_variant_id JOIN products p ON p.id = pv.product_id WHERE ri.restock_id = restocks.id AND ri.deleted_at IS NULL)';

        $paginator = Restock::query()
            ->select(['id', 'created_by_id', 'total', 'notes', 'stock_type', 'created_at'])
            ->with([
                'createdBy:id',
                'createdBy.userProfile:id,user_id,full_name',
            ])
            ->selectRaw("{$itemsCountQuery} as items_count")
            ->selectRaw("{$totalQtyQuery} as total_qty")
            ->selectRaw("{$productNamesQuery} as product_names")
            ->when($highlight, fn ($q) => $q->where('id', $highlight))
            ->when($search, function ($q) use ($search) {
                $q->whereHas('restockItems.productVariant.product', fn ($sq) => $sq->where('name', 'like', "%{$search}%"))
                    ->orWhere('notes', 'like', "%{$search}%");
            })
            ->orderBy($sort, $direction)
            ->paginate($perPage);

        $paginator->getCollection()->each(function (Restock $restock) {
            $restockMedia = $restock->getMedia('photos');
            $restock->photo_urls = $restockMedia->map(
                fn ($media) => $this->s3Service->getTemporaryUrl($media->getPath())
            )->toArray();
            $restock->photo_conversion_urls = $restockMedia->map(
                fn ($media) => $media->getGeneratedConversions()->contains('thumb')
                    ? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
                    : $this->s3Service->getTemporaryUrl($media->getPath())
            )->toArray();
        });

        return $paginator;
    }

    public function getForEdit(Restock $restock): array
    {
        $restock->load([
            'restockItems' => fn ($q) => $q
                ->select(['id', 'restock_id', 'product_variant_id', 'quantity', 'unit_price', 'subtotal']),
            'restockItems.productVariant:id,name',
        ]);

        $media = $restock->getFirstMedia('photos');

        return [
            'id' => $restock->id,
            'stock_type' => $restock->stock_type->value,
            'notes' => $restock->notes,
            'photo_key' => $media?->getCustomProperty('s3_key') ?? $media?->file_name,
            'photo_url' => $media ? $this->s3Service->getTemporaryUrl($media->getPath()) : null,
            'items' => $restock->restockItems->map(fn ($item) => [
                'id' => $item->id,
                'product_variant_id' => $item->product_variant_id,
                'quantity' => $item->quantity,
                'unit_price' => $item->unit_price,
            ]),
        ];
    }

    public function getItems(Restock $restock): \Illuminate\Support\Collection
    {
        return $restock->restockItems()
            ->select(['id', 'restock_id', 'product_variant_id', 'quantity', 'unit_price', 'subtotal'])
            ->with(['productVariant:id,product_id,name', 'productVariant.product:id,name'])
            ->orderByRaw('(SELECT name FROM product_variants WHERE product_variants.id = restock_items.product_variant_id)')
            ->get()
            ->each(function (RestockItem $item) {
                if (! $item->productVariant) {
                    return;
                }

                $media = $item->productVariant->getFirstMedia('images');
                $item->productVariant->photo_url = $media
                    ? $this->s3Service->getTemporaryUrl($media->getPath())
                    : null;
                $item->productVariant->photo_conversion_url = $media
                    ? ($media->getGeneratedConversions()->contains('thumb')
                        ? $this->s3Service->getTemporaryUrl($media->getPath('thumb'))
                        : $this->s3Service->getTemporaryUrl($media->getPath()))
                    : null;
            });
    }

    public function store(array $data): Restock
    {
        return DB::transaction(function () use ($data) {
            $now = now();
            $total = 0;
            $stockType = $data['stock_type'] ?? ProductStockQuality::GOOD->value;

            $itemRows = $this->buildItemRows($data['items'], $stockType, $now, $total);

            $restock = Restock::create([
                'created_by_id' => auth()->id(),
                'total' => $total,
                'notes' => $data['notes'] ?? null,
                'stock_type' => $stockType,
            ]);

            foreach ($itemRows as &$row) {
                $row['restock_id'] = $restock->id;
            }
            DB::table('restock_items')->insert($itemRows);

            $this->applyStock($data['items'], $stockType, 1);
            $this->syncPhoto($restock, $data);

            NotificationService::notify(
                roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
                title: 'Restock Baru',
                body: 'Restock '.($stockType === ProductStockQuality::GOOD->value ? 'produk' : 'reject').' sebesar Rp '.number_format($total, 0, ',', '.').' berhasil dicatat oleh '.auth()->user()->full_name.'.',
                url: route('admin.manage.restocks.index', ['highlight' => $restock->id]),
            );

            return $restock;
        });
    }

    public function update(Restock $restock, array $data): Restock
    {
        $restock = DB::transaction(function () use ($restock, $data) {
            $restock->load('restockItems');

            $restock->restockItems->each(function (RestockItem $item) use ($restock) {
                $this->adjustVariantStock($item->product_variant_id, $item->quantity, -1, $restock->stock_type->value);
            });

            $restock->restockItems()->delete();

            $now = now();
            $total = 0;
            $stockType = $data['stock_type'] ?? $restock->stock_type->value;

            $itemRows = $this->buildItemRows($data['items'], $stockType, $now, $total);

            foreach ($itemRows as &$row) {
                $row['restock_id'] = $restock->id;
            }
            DB::table('restock_items')->insert($itemRows);

            $restock->update([
                'total' => $total,
                'notes' => $data['notes'] ?? null,
                'stock_type' => $stockType,
            ]);

            $this->applyStock($data['items'], $stockType, 1);
            $this->syncPhoto($restock, $data);

            return $restock;
        });

        NotificationService::notify(
            roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
            title: 'Restock Diperbarui',
            body: 'Restock berhasil diperbarui oleh '.auth()->user()->full_name.'.',
            url: route('admin.manage.restocks.index', ['highlight' => $restock->id]),
        );

        return $restock;
    }

    public function destroy(Restock $restock): bool
    {
        $result = DB::transaction(function () use ($restock) {
            $restock->load('restockItems');

            $restock->restockItems->each(function (RestockItem $item) use ($restock) {
                $this->adjustVariantStock($item->product_variant_id, $item->quantity, -1, $restock->stock_type->value);
            });

            $restock->restockItems()->delete();
            $restock->delete();

            return true;
        });

        NotificationService::notify(
            roles: [Role::OWNER, Role::DEVELOPER, Role::ADMIN_TOKO],
            title: 'Restock Dihapus',
            body: 'Restock berhasil dihapus oleh '.auth()->user()->full_name.'.',
            url: route('admin.manage.restocks.index'),
        );

        return $result;
    }

    private function buildItemRows(array $items, string $stockType, $now, int &$total): array
    {
        $priceType = $stockType === ProductStockQuality::REJECT->value
            ? PriceType::REJECT_CAPITAL
            : PriceType::CAPITAL;

        $variantIds = collect($items)->pluck('product_variant_id')->unique()->all();
        $prices = ProductVariant::query()
            ->whereKey($variantIds)
            ->with('productPrices:id,variant_id,type,price')
            ->get()
            ->mapWithKeys(function (ProductVariant $variant) use ($priceType) {
                $price = $variant->productPrices
                    ->first(fn ($p) => $p->type === $priceType);

                return [$variant->id => $price?->price ?? 0];
            });

        return collect($items)->map(function ($item) use ($now, $prices, &$total) {
            $quantity = (int) $item['quantity'];
            $unitPrice = (int) ($prices[$item['product_variant_id']] ?? 0);
            $itemSubtotal = $unitPrice * $quantity;
            $total += $itemSubtotal;

            return [
                'restock_id' => null,
                'user_id' => auth()->id(),
                'product_variant_id' => $item['product_variant_id'],
                'quantity' => $quantity,
                'unit_price' => $unitPrice,
                'subtotal' => $itemSubtotal,
                'created_at' => $now,
                'updated_at' => $now,
            ];
        })->toArray();
    }
}
