<?php

namespace App\Services;

use App\Traits\EnsuresVehicleAttribute;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\DrivetrainType;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\Services\FinanceService;
use Mtc\MercuryDataModels\Services\FinanceServiceHelper;
use Mtc\MercuryDataModels\TransmissionType;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;

class VehicleBulkUpdateService
{
    use EnsuresVehicleAttribute;

    private const EXCLUDED_FIELDS = [
        'uuid',
        'vin',
        'auto_trader_id',
        'cap_id',
        'cap_code',
        'motor_check_id',
        'stock_provider',
        'vrm_condensed',
        'data',
        'was_recently_synced',
        'pending_stock_sync',
        'lat',
        'lng',
        'finance_exported_at',
    ];

    private const BOOLEAN_FIELDS = [
        'is_published',
        'is_new',
        'is_sold',
        'is_reserved',
        'is_demo',
        'is_vat_applicable',
        'featured',
        'hide_finance_calculator',
        'personalized_number_plate',
    ];

    private const INTEGER_FIELDS = [
        'door_count',
        'seats',
        'manufacture_year',
        'odometer_km',
        'odometer_mi',
        'engine_size_cc',
        'co2',
        'previous_owner_count',
        'battery_range',
        'battery_capacity_kwh',
        'battery_usable_capacity_kwh',
        'battery_charge_time',
        'battery_quick_charge_time',
        'battery_quick_charge_level',
        'battery_quick_charge_start_level',
        'wheelbase_mm',
        'payload_kg',
        'gross_vehicle_weight_kg',
        'bhp',
        'vehicle_length',
    ];

    private const DECIMAL_FIELDS = [
        'price',
        'admin_fee',
        'original_price',
        'previous_price',
        'monthly_price',
        'rrp_price',
        'deposit',
        'mpg',
    ];

    private const DATE_FIELDS = [
        'first_registration_date',
        'published_at',
        'sold_at',
        'reserved_at',
        'available_date',
        'deleted_at',
        'stock_arrival_date',
    ];

    private const RELATIONSHIP_FIELDS = [
        'make_id' => [
            'label' => 'Make',
            'model' => VehicleMake::class,
            'label_column' => 'name',
        ],
        'model_id' => [
            'label' => 'Model',
            'model' => VehicleModel::class,
            'label_column' => 'name',
        ],
        'dealership_id' => [
            'label' => 'Dealership',
            'model' => Dealership::class,
            'label_column' => 'name',
        ],
        'transmission_id' => [
            'label' => 'Transmission',
            'model' => TransmissionType::class,
            'label_column' => 'name',
        ],
        'fuel_type_id' => [
            'label' => 'Fuel Type',
            'model' => FuelType::class,
            'label_column' => 'name',
        ],
        'body_style_id' => [
            'label' => 'Body Style',
            'model' => BodyStyleType::class,
            'label_column' => 'name',
        ],
        'drivetrain_id' => [
            'label' => 'Drivetrain',
            'model' => DrivetrainType::class,
            'label_column' => 'name',
        ],
    ];

    private const FIELD_LABELS = [
        'title' => 'Title',
        'derivative' => 'Derivative',
        'registration_number' => 'Registration Number',
        'colour' => 'Colour',
        'description' => 'Description',
        'attention_grabber' => 'Attention Grabber',
        'trim' => 'Trim',
        'main_video_url' => 'Main Video URL',
        'exterior_video_url' => 'Exterior Video URL',
        'interior_video_url' => 'Interior Video URL',
        'battery_slow_charge_description' => 'Slow Charge Description',
        'battery_quick_charge_description' => 'Quick Charge Description',
        'plug_type' => 'Plug Type',
        'rapid_charge_plug_type' => 'Rapid Charge Plug Type',
        'wheelbase_type' => 'Wheelbase Type',
        'monthly_cost_type' => 'Monthly Cost Type',
        'type' => 'Vehicle Type',
        'is_published' => 'Published',
        'is_new' => 'New Vehicle',
        'is_sold' => 'Sold',
        'is_reserved' => 'Reserved',
        'is_demo' => 'Demo Vehicle',
        'is_vat_applicable' => 'VAT Applicable',
        'featured' => 'Featured',
        'hide_finance_calculator' => 'Hide Finance Calculator',
        'personalized_number_plate' => 'Personalized Number Plate',
        'door_count' => 'Door Count',
        'seats' => 'Seats',
        'manufacture_year' => 'Manufacture Year',
        'odometer_km' => 'Odometer (km)',
        'odometer_mi' => 'Odometer (mi)',
        'engine_size_cc' => 'Engine Size (cc)',
        'co2' => 'CO2',
        'previous_owner_count' => 'Previous Owners',
        'battery_range' => 'Battery Range',
        'battery_capacity_kwh' => 'Battery Capacity (kWh)',
        'battery_usable_capacity_kwh' => 'Usable Battery Capacity (kWh)',
        'battery_charge_time' => 'Battery Charge Time',
        'battery_quick_charge_time' => 'Quick Charge Time',
        'battery_quick_charge_level' => 'Quick Charge Level',
        'battery_quick_charge_start_level' => 'Quick Charge Start Level',
        'wheelbase_mm' => 'Wheelbase (mm)',
        'payload_kg' => 'Payload (kg)',
        'gross_vehicle_weight_kg' => 'Gross Vehicle Weight (kg)',
        'bhp' => 'BHP',
        'vehicle_length' => 'Vehicle Length',
        'price' => 'Price',
        'admin_fee' => 'Admin Fee',
        'original_price' => 'Original Price',
        'previous_price' => 'Previous Price',
        'monthly_price' => 'Monthly Price',
        'rrp_price' => 'RRP Price',
        'deposit' => 'Deposit',
        'mpg' => 'MPG',
        'alternate_finance_key' => 'Alternate Finance Key',
        'first_registration_date' => 'First Registration Date',
        'published_at' => 'Published At',
        'sold_at' => 'Sold At',
        'reserved_at' => 'Reserved At',
        'available_date' => 'Available Date',
        'deleted_at' => 'Archived At',
        'stock_arrival_date' => 'Stock Arrival Date',
    ];

    public function getFields(): array
    {
        $vehicle = new Vehicle();
        $fillable = $vehicle->getFillable();
        $fields = [];

        foreach ($fillable as $field) {
            if (in_array($field, self::EXCLUDED_FIELDS)) {
                continue;
            }

            $fieldData = $this->getFieldMetadata($field);
            if ($fieldData) {
                $fields[] = $fieldData;
            }
        }

        $attributes = VehicleAttribute::query()->where('model', 'vehicle')->get();
        foreach ($attributes as $attribute) {
            $fields[] = $this->getAttributeFieldMetadata($attribute);
        }

        usort($fields, fn ($a, $b) => strcmp($a['label'], $b['label']));

        return $fields;
    }

    public function getAllowedFields(): array
    {
        return array_column($this->getFields(), 'name');
    }

    public function getVehicleData(array $vehicleIds): array
    {
        $allowedFields = $this->getAllowedFields();
        $modelFields = array_filter($allowedFields, fn ($f) => !$this->isAttributeField($f));

        return Vehicle::query()
            ->whereIn('id', $vehicleIds)
            ->with('attributeValues')
            ->get()
            ->mapWithKeys(function (Vehicle $vehicle) use ($modelFields, $allowedFields) {
                $data = collect($vehicle->toArray())
                    ->only($modelFields)
                    ->toArray();

                foreach ($allowedFields as $field) {
                    if ($this->isAttributeField($field)) {
                        $attributeId = $this->getAttributeId($field);
                        $attributeValue = $vehicle->attributeValues
                            ->where('attribute_id', $attributeId)
                            ->first();
                        $data[$field] = $attributeValue?->getValue() ?? null;
                    }
                }

                return [$vehicle->id => $data];
            })
            ->toArray();
    }

    private function getFieldMetadata(string $field): ?array
    {
        $base = [
            'name' => $field,
            'label' => self::FIELD_LABELS[$field] ?? $this->generateLabel($field),
            'nullable' => true,
        ];

        if (array_key_exists($field, self::RELATIONSHIP_FIELDS)) {
            $config = self::RELATIONSHIP_FIELDS[$field];
            return array_merge($base, [
                'type' => 'select',
                'label' => $config['label'],
                'options' => $this->getRelationshipOptions($config['model'], $config['label_column']),
            ]);
        }

        if ($field === 'type') {
            return array_merge($base, [
                'type' => 'select',
                'options' => [
                    ['value' => 'car', 'label' => 'Car'],
                    ['value' => 'lcv', 'label' => 'LCV'],
                    ['value' => 'motorcycle', 'label' => 'Motorcycle'],
                ],
            ]);
        }

        if (in_array($field, self::BOOLEAN_FIELDS)) {
            return array_merge($base, [
                'type' => 'boolean',
                'nullable' => false,
            ]);
        }

        if (in_array($field, self::INTEGER_FIELDS)) {
            return array_merge($base, [
                'type' => 'integer',
            ]);
        }

        if (in_array($field, self::DECIMAL_FIELDS)) {
            return array_merge($base, [
                'type' => 'decimal',
            ]);
        }

        if (in_array($field, self::DATE_FIELDS)) {
            return array_merge($base, [
                'type' => 'date',
            ]);
        }

        return array_merge($base, [
            'type' => 'string',
        ]);
    }

    private function getRelationshipOptions(string $modelClass, string $labelColumn): array
    {
        return $modelClass::query()
            ->select(['id', $labelColumn])
            ->orderBy($labelColumn)
            ->get()
            ->map(fn ($item) => [
                'value' => $item->id,
                'label' => $item->{$labelColumn},
            ])
            ->toArray();
    }

    private function generateLabel(string $field): string
    {
        return ucwords(str_replace('_', ' ', $field));
    }

    public function update(string $mode, array $vehicleIds, array $data): int
    {
        $allowedFields = $this->getAllowedFields();

        return DB::transaction(function () use ($mode, $vehicleIds, $data, $allowedFields) {
            if ($mode === 'same') {
                return $this->updateSameValue($vehicleIds, $data['field'], $data['value'], $allowedFields);
            }

            return $this->updateDifferentValues($vehicleIds, $data['updates'], $allowedFields);
        });
    }

    private function updateSameValue(array $vehicleIds, string $field, mixed $value, array $allowedFields): int
    {
        if (!in_array($field, $allowedFields)) {
            throw new \InvalidArgumentException("Field '{$field}' is not allowed for bulk update.");
        }

        if ($this->isAttributeField($field)) {
            $attributeId = $this->getAttributeId($field);
            $attribute = VehicleAttribute::query()->find($attributeId);
            if (!$attribute) {
                return 0;
            }

            $vehicles = Vehicle::query()->whereIn('id', $vehicleIds)->get();
            foreach ($vehicles as $vehicle) {
                $this->updateAttributeValue($vehicle, $attribute, $value);
            }

            return $vehicles->count();
        }

        $value = $this->castValue($field, $value);

        return Vehicle::query()
            ->whereIn('id', $vehicleIds)
            ->update([$field => $value]);
    }

    private function updateDifferentValues(array $vehicleIds, array $updates, array $allowedFields): int
    {
        $updatedCount = 0;

        foreach ($updates as $vehicleId => $fields) {
            if (!in_array((int) $vehicleId, $vehicleIds)) {
                continue;
            }

            $modelFields = [];
            $attributeFields = [];

            foreach ($fields as $field => $value) {
                if (!in_array($field, $allowedFields)) {
                    continue;
                }

                if ($this->isAttributeField($field)) {
                    $attributeFields[$field] = $value;
                } else {
                    $modelFields[$field] = $this->castValue($field, $value);
                }
            }

            $updated = false;

            if (!empty($modelFields)) {
                $result = Vehicle::query()
                    ->where('id', $vehicleId)
                    ->update($modelFields);
                if ($result) {
                    $updated = true;
                }
            }

            if (!empty($attributeFields)) {
                $vehicle = Vehicle::query()->find($vehicleId);
                if ($vehicle) {
                    foreach ($attributeFields as $field => $value) {
                        $attributeId = $this->getAttributeId($field);
                        $attribute = VehicleAttribute::query()->find($attributeId);
                        if ($attribute) {
                            $this->updateAttributeValue($vehicle, $attribute, $value);
                            $updated = true;
                        }
                    }
                }
            }

            if ($updated) {
                $updatedCount++;
            }
        }

        return $updatedCount;
    }

    private function castValue(string $field, mixed $value): mixed
    {
        if ($value === null || $value === '') {
            return null;
        }

        if (in_array($field, self::BOOLEAN_FIELDS)) {
            return filter_var($value, FILTER_VALIDATE_BOOLEAN);
        }

        if (in_array($field, self::INTEGER_FIELDS)) {
            return (int) $value;
        }

        if (in_array($field, self::DECIMAL_FIELDS)) {
            return (float) $value;
        }

        return $value;
    }

    public function deleteFinanceExamples(array $vehicleIds): int
    {
        $deletedCount = 0;

        $vehicles = Vehicle::query()->whereIn('id', $vehicleIds)->get();

        foreach ($vehicles as $vehicle) {
            $deleted = $vehicle->financeExamples()->delete();
            if ($deleted > 0) {
                $deletedCount++;
            }
        }

        return $deletedCount;
    }

    /**
     * Soft-delete the selected vehicles via per-model delete() so SoftDeletes
     * events and audit rows fire. Already-archived rows are skipped.
     */
    public function archive(array $vehicleIds): int
    {
        $archived = 0;
        $vehicles = Vehicle::query()->whereIn('id', $vehicleIds)->get();

        foreach ($vehicles as $vehicle) {
            if ($vehicle->trashed()) {
                continue;
            }
            $vehicle->delete();
            $archived++;
        }

        return $archived;
    }

    public function requestFinanceQuotes(array $vehicleIds): array
    {
        $requested = 0;
        $failed = 0;

        if (!FinanceServiceHelper::hasEnabledProvider()) {
            return ['requested' => 0, 'failed' => count($vehicleIds)];
        }

        $financeService = new FinanceService();
        $vehicles = Vehicle::query()->whereIn('id', $vehicleIds)->get();

        foreach ($vehicles as $vehicle) {
            try {
                if (!empty($vehicle->data['stop_finance_sync'])) {
                    $failed++;
                    continue;
                }
                if ($financeService->request($vehicle, [], true)) {
                    $requested++;
                } else {
                    $failed++;
                }
            } catch (\Exception $e) {
                Log::error('Bulk finance quote request failed', [
                    'tenant_id' => tenant('id'),
                    'vehicle_id' => $vehicle->id,
                    'error' => $e->getMessage(),
                ]);
                $failed++;
            }
        }

        return ['requested' => $requested, 'failed' => $failed];
    }

    private function getAttributeFieldMetadata(VehicleAttribute $attribute): array
    {
        $type = match ($attribute->type) {
            'text', 'textarea', 'editor' => 'string',
            'number' => 'decimal',
            'integer' => 'integer',
            'boolean' => 'boolean',
            'datetime' => 'date',
            'select' => 'select',
            default => 'string',
        };

        $field = [
            'name' => "attribute::{$attribute->id}",
            'label' => $attribute->name,
            'type' => $type,
            'nullable' => true,
        ];

        if ($type === 'select' && !empty($attribute->data)) {
            $field['options'] = collect($attribute->data)->map(fn ($option) => [
                'value' => $option,
                'label' => $option,
            ])->toArray();
        }

        if ($type === 'boolean') {
            $field['nullable'] = false;
        }

        return $field;
    }

    private function isAttributeField(string $field): bool
    {
        return str_starts_with($field, 'attribute::');
    }

    private function getAttributeId(string $field): int
    {
        return (int) str_replace('attribute::', '', $field);
    }

    private function updateAttributeValue(Vehicle $vehicle, VehicleAttribute $attribute, mixed $value): void
    {
        $column = $this->attributeValueColumn($attribute->toArray());

        if ($value === null || $value === '') {
            $vehicle->attributeValues()
                ->where('attribute_id', $attribute->id)
                ->delete();
            return;
        }

        VehicleAttributeValue::query()->updateOrCreate(
            [
                'attribute_id' => $attribute->id,
                'owner_id' => $vehicle->id,
                'owner_type' => $vehicle->getMorphClass(),
                'index' => 0,
            ],
            [
                'type' => $attribute->type,
                'slug' => $attribute->slug,
                $column => $value,
            ]
        );
    }
}
