<?php

namespace App\Services\Pricing;

use App\Models\Tenant\Client;
use App\Models\Tenant\MarkupRule;
use App\Models\Tenant\Part;
use App\Models\Tenant\PricingCoefficient;
use App\Models\Tenant\Vehicle;

/**
 * Computes a contextual sell price for a part:
 *   base = MarkupRule applied to buy_price (or current sell_price fallback)
 *   final = base × product(matching coefficient multipliers)
 *
 * Coefficient stacking:
 *   - stackable coefficients all multiply together
 *   - among non-stackable matches, only the single highest multiplier applies
 */
class PricingEngine
{
    /**
     * @return array{base: float, final: float, applied: array<int, array{name:string, multiplier:float}>}
     */
    public function quote(
        Part $part,
        ?Vehicle $vehicle = null,
        ?Client $client = null,
        string $urgency = 'normal',
    ): array {
        $base = $this->basePrice($part);

        $ctx = [
            'class' => $this->vehicleClass($vehicle),
            'age' => $this->vehicleAge($vehicle),
            'body_type' => $vehicle?->body_type,
            'transmission' => $vehicle?->transmission_type,
            'vip' => (bool) ($client?->is_vip),
            'urgency' => $urgency ?: 'normal',
        ];

        $coefficients = PricingCoefficient::where('is_active', true)
            ->orderBy('priority')
            ->get()
            ->filter(fn (PricingCoefficient $c) => $c->matches($ctx));

        $applied = [];
        $factor = 1.0;

        // Stackable: multiply all.
        foreach ($coefficients->where('stackable', true) as $c) {
            $factor *= (float) $c->multiplier;
            $applied[] = ['name' => $c->name, 'multiplier' => (float) $c->multiplier];
        }

        // Non-stackable: take only the strongest one.
        $nonStack = $coefficients->where('stackable', false)
            ->sortByDesc(fn ($c) => (float) $c->multiplier)
            ->first();
        if ($nonStack) {
            $factor *= (float) $nonStack->multiplier;
            $applied[] = ['name' => $nonStack->name, 'multiplier' => (float) $nonStack->multiplier];
        }

        $result = [
            'base' => round($base, 2),
            'final' => round($base * $factor, 2),
            'applied' => $applied,
            'context' => $ctx,
        ];

        return $result;
    }

    /**
     * Persist a quote to pricing_application_logs — appends one immutable row
     * per pricing decision. Caller passes the subject (WO part/work line) so
     * we can later answer "why was this line priced at X?".
     */
    public function logApplication(array $quote, $subject, ?Vehicle $vehicle = null, ?Client $client = null, ?Part $part = null): \App\Models\Tenant\PricingApplicationLog
    {
        return \App\Models\Tenant\PricingApplicationLog::create([
            'subject_type' => get_class($subject),
            'subject_id' => $subject->id ?? 0,
            'part_id' => $part?->id,
            'vehicle_id' => $vehicle?->id,
            'client_id' => $client?->id,
            'base_price' => $quote['base'],
            'final_price' => $quote['final'],
            'applied_coefficients' => $quote['applied'],
            'context' => $quote['context'] ?? [],
            'calculated_at' => now(),
        ]);
    }

    private function basePrice(Part $part): float
    {
        $rule = MarkupRule::bestForPart($part);
        if ($rule) {
            return (float) $part->buy_price * (1 + (float) $rule->markup_pct / 100);
        }
        // Fall back to existing sell_price, or buy_price + 30%.
        if ((float) $part->sell_price > 0) return (float) $part->sell_price;
        return (float) $part->buy_price * 1.30;
    }

    /** Explicit vehicle_class, else inferred from fuel (hybrid/EV). */
    private function vehicleClass(?Vehicle $vehicle): ?string
    {
        if (! $vehicle) return null;
        if ($vehicle->vehicle_class) return $vehicle->vehicle_class;

        $fuel = mb_strtolower((string) $vehicle->fuel);
        if (str_contains($fuel, 'hybrid') || str_contains($fuel, 'hibrid')) return 'hybrid';
        if (str_contains($fuel, 'electric') || $fuel === 'ev' || str_contains($fuel, 'electr')) return 'ev';

        return null;
    }

    private function vehicleAge(?Vehicle $vehicle): ?int
    {
        if (! $vehicle || ! $vehicle->year) return null;
        return max(0, (int) date('Y') - (int) $vehicle->year);
    }
}
