<?php

namespace Modules\LandedCost\Entities\Drivers;

use App\PurchaseLine;
use App\Transaction;
use App\Currency;
use Illuminate\Support\Collection;
use Modules\LandedCost\Entities\LcLandedCost;

/**
 * Driver for core Purchases (type=purchase) and Purchase Orders (type=purchase_order).
 *
 * Core purchase_lines store purchase_price / purchase_price_inc_tax ALREADY converted
 * to the business base currency (ProductUtil::createOrUpdatePurchaseLines multiplies by
 * the transaction exchange_rate), so the line-level rate is effectively 1 and the
 * supplier-currency landed cost equals the base-currency landed cost. Landed costs are
 * persisted to the generic lc_landed_costs table.
 */
class CoreTransactionDriver extends AbstractLandedCostDriver
{
    /** @var Transaction */
    protected $transaction;

    /** @var string 'purchase' | 'purchase_order' */
    protected $type;

    public function __construct($transactionId, string $type)
    {
        $this->type = $type;

        $query = Transaction::where('id', $transactionId)->where('type', $type);
        if ($businessId = session('business.id')) {
            $query->where('business_id', $businessId);
        }
        $this->transaction = $query->firstOrFail();
    }

    public function authorize(): void
    {
        if (!auth()->check() || !auth()->user()->can('landed_cost.manage')) {
            abort(403);
        }
    }

    public function parentType(): string
    {
        return $this->type;
    }

    public function parentId(): int
    {
        return (int) $this->transaction->id;
    }

    public function businessId(): ?int
    {
        return $this->transaction->business_id ? (int) $this->transaction->business_id : null;
    }

    public function subjectModel(): ?\Illuminate\Database\Eloquent\Model
    {
        return $this->transaction;
    }

    public function getLines(): Collection
    {
        return PurchaseLine::where('transaction_id', $this->transaction->id)
            ->with('product', 'variations')
            ->get();
    }

    public function baseSymbol(): string
    {
        return session('currency')['symbol'] ?? '';
    }

    public function currencyOptions(): array
    {
        return Currency::orderBy('currency')->get()->map(function ($c) {
            return [
                'id'     => $c->id,
                'name'   => $c->currency . ' (' . $c->code . ')',
                'symbol' => $c->symbol,
                'rate'   => 1,
            ];
        })->toArray();
    }

    public function buildPreviewRow(array $row, $line): array
    {
        $qty             = (float) $line->quantity;
        $basePrice       = (float) $line->purchase_price_inc_tax; // already base currency
        $unitExpenseBase = $qty > 0 ? ((float) $row['allocated_base_amount'] / $qty) : 0;
        $landed          = $basePrice + $unitExpenseBase;

        return [
            'purchase_line_id'   => $row['purchase_line_id'],
            'product_name'       => $this->productLabel($line->product, $line->variations),
            'order_qty'          => $qty,
            'distribution_ratio' => round($row['distribution_ratio'] * 100, 2),
            'allocated_amount'   => $row['allocated_amount'],
            'allocated_base'     => $row['allocated_base_amount'],
            'base_price'         => round($basePrice, 4),
            'landed_cost'        => round($landed, 4),
            'landed_cost_base'   => round($landed, 4),
            'currency_symbol'    => $this->baseSymbol(),
        ];
    }

    public function recalculateLandedCosts(): void
    {
        $allocations = $this->allocationsByLine();

        foreach ($this->getLines() as $line) {
            $qty             = (float) $line->quantity;
            $totalBase       = (float) ($allocations[$line->id] ?? 0);
            $unitExpenseBase = $qty > 0 ? ($totalBase / $qty) : 0;
            $basePrice       = (float) $line->purchase_price_inc_tax;
            $landed          = $basePrice + $unitExpenseBase;

            LcLandedCost::updateOrCreate(
                [
                    'parent_type'      => $this->parentType(),
                    'parent_id'        => $this->parentId(),
                    'purchase_line_id' => $line->id,
                ],
                [
                    'base_unit_price'       => round($basePrice, 4),
                    'landed_unit_cost'      => round($landed, 4),
                    'landed_unit_cost_base' => round($landed, 4),
                ]
            );
        }
    }

    public function getLandedCostData(): array
    {
        $result = [];
        $rows = LcLandedCost::forParent($this->parentType(), $this->parentId())->get();
        foreach ($rows as $r) {
            $result[$r->purchase_line_id] = [
                'landed_unit_cost'     => (float) $r->landed_unit_cost,
                'landed_unit_cost_base'=> (float) $r->landed_unit_cost_base,
            ];
        }
        return $result;
    }
}
