<?php

namespace App\Models\Tenant;

use App\Models\Concerns\BelongsToTenant;
use App\Models\Concerns\Auditable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class WorkOrder extends Model implements HasMedia
{
    use Auditable, BelongsToTenant, InteractsWithMedia, SoftDeletes;

    public const STATUSES = [
        'new' => 'Nou',
        'diagnosis' => 'Diagnosticare',
        'agreement' => 'Aprobare client',
        'approved' => 'Aprobat',
        'in_work' => 'În lucru',
        'awaiting_parts' => 'Așteaptă piese',
        'ready' => 'Gata de ridicare',
        'done' => 'Predat',
        'cancelled' => 'Anulat',
    ];

    public const PAY_STATUSES = [
        'unpaid' => 'Neplătit',
        'partial' => 'Parțial',
        'paid' => 'Plătit',
    ];

    protected $fillable = [
        'company_id', 'number',
        'client_id', 'vehicle_id', 'master_id', 'deal_id', 'appointment_id',
        'opened_at', 'closed_at', 'mileage_in', 'mileage_out',
        'complaint', 'diagnosis', 'recommendations',
        'status', 'urgency', 'pay_status', 'approved', 'approved_at',
        'discount_pct', 'override_margin_pct', 'apply_margin', 'total',
        'eta_at', 'eta_promised', 'eta_change_reason', 'eta_updated_at',
        'tracking_token',
    ];

    protected $casts = [
        'opened_at' => 'date',
        'closed_at' => 'date',
        'approved_at' => 'datetime',
        'eta_at' => 'datetime',
        'eta_promised' => 'datetime',
        'eta_updated_at' => 'datetime',
        'approved' => 'boolean',
        'apply_margin' => 'boolean',
        'discount_pct' => 'decimal:2',
        'total' => 'decimal:2',
    ];

    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('photos');
    }

    public function trackingUrl(): string
    {
        return url('/t/' . $this->tracking_token);
    }

    public function client(): BelongsTo
    {
        return $this->belongsTo(Client::class);
    }

    public function vehicle(): BelongsTo
    {
        return $this->belongsTo(Vehicle::class);
    }

    public function master(): BelongsTo
    {
        return $this->belongsTo(User::class, 'master_id');
    }

    public function works(): HasMany
    {
        return $this->hasMany(WorkOrderWork::class);
    }

    public function parts(): HasMany
    {
        return $this->hasMany(WorkOrderPart::class);
    }

    public function payments(): HasMany
    {
        return $this->hasMany(Payment::class);
    }

    public function subcontractJobs(): HasMany
    {
        return $this->hasMany(SubcontractJob::class);
    }

    public function paidAmount(): float
    {
        return (float) $this->payments()->sum('amount');
    }

    public function balanceDue(): float
    {
        return max(0.0, (float) $this->total - $this->paidAmount());
    }

    public function recalcTotal(): void
    {
        $worksTotal = $this->works()->sum('total');
        $partsTotal = $this->parts()->sum('total');
        $subcontractTotal = $this->subcontractJobs()
            ->where('status', '!=', 'cancelled')
            ->sum('client_price');
        $sub = (float) $worksTotal + (float) $partsTotal + (float) $subcontractTotal;
        $disc = (float) $this->discount_pct;
        $this->total = round($sub * (1 - $disc / 100), 2);
        $this->save();
    }

    public static function generateNumber(int $companyId): string
    {
        $year = date('y');
        $count = static::withoutGlobalScopes()
            ->where('company_id', $companyId)
            ->whereYear('created_at', date('Y'))
            ->count();
        return sprintf('WO-%s-%04d', $year, $count + 1);
    }

    /** Auto-send 'ready' email + broadcast WS event on status change. */
    protected static function booted(): void
    {
        static::creating(function (self $wo) {
            if (empty($wo->tracking_token)) {
                $wo->tracking_token = \Illuminate\Support\Str::random(24);
            }
        });

        static::updated(function (self $wo) {
            if (
                $wo->wasChanged('status')
                && $wo->status === 'ready'
                && $wo->getOriginal('status') !== 'ready'
            ) {
                app(\App\Services\NotificationDispatcher::class)->workOrderReady($wo);
            }

            // WO.apply_margin sau override_margin_pct s-a schimbat → recomputăm salary_base
            // pe toate manoperele existente (spre deosebire de user.internal_margin_pct,
            // decizia la nivel de Fișă e explicită și trebuie să afecteze toate liniile ei).
            if ($wo->wasChanged(['apply_margin', 'override_margin_pct'])) {
                $resolver = app(\App\Services\MarginResolver::class);
                foreach ($wo->works()->get() as $line) {
                    if ($wo->apply_margin === false) {
                        $line->applied_margin_pct = 0;
                        $line->salary_base = (float) $line->total;
                    } else {
                        $mechanic = $line->master_id ? \App\Models\Tenant\User::find($line->master_id) : null;
                        $marginPct = $resolver->resolve($wo, $mechanic);
                        $line->applied_margin_pct = $marginPct;
                        $line->salary_base = $resolver->computeSalaryBase((float) $line->total, $marginPct);
                    }
                    $line->saveQuietly();
                }
            }

            // Push the assigned mechanic when a WO gets assigned to them.
            if ($wo->wasChanged('master_id') && $wo->master_id) {
                try {
                    app(\App\Services\Notifications\WebPushService::class)->sendToUser(
                        (int) $wo->master_id,
                        'Fișă nouă atribuită',
                        "Fișa #{$wo->number} · " . ($wo->vehicle?->plate ?? ''),
                        '/app/resources/work-orders/' . $wo->id . '/edit',
                        'wo-assign-' . $wo->id,
                    );
                } catch (\Throwable $e) {
                    \Illuminate\Support\Facades\Log::debug('WO assign push skipped: ' . $e->getMessage());
                }
            }

            // Warehouse lifecycle: status=done → consume reservations into issues;
            // status=cancelled → release reservations.
            if ($wo->wasChanged('status')) {
                $svc = app(\App\Services\Warehouse\WarehouseService::class);
                if ($wo->status === 'done' && $wo->getOriginal('status') !== 'done') {
                    $svc->consume($wo);
                }
                if ($wo->status === 'cancelled' && $wo->getOriginal('status') !== 'cancelled') {
                    foreach ($wo->parts as $wop) {
                        $svc->release($wop);
                    }
                }
            }

            // Broadcast real-time update on any field change (skip if broadcasting=log).
            if (config('broadcasting.default') !== 'log') {
                try {
                    $company = \App\Models\Central\Company::withoutGlobalScopes()->find($wo->company_id);
                    if ($company) {
                        \App\Events\WorkOrderUpdated::dispatch($wo, $company->slug);
                    }
                } catch (\Throwable $e) {
                    \Illuminate\Support\Facades\Log::debug('WO broadcast skipped: ' . $e->getMessage());
                }
            }
        });
    }
}
