<?php

namespace App\Services;

use App\Traits\EnsuresVehicleAttribute;
use Illuminate\Support\Facades\DB;
use Mtc\ContentManager\Models\Template;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\DrivetrainType;
use Mtc\MercuryDataModels\Form;
use Mtc\MercuryDataModels\Franchise;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\OfferType;
use Mtc\MercuryDataModels\TransmissionType;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleMake;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;

class OfferBulkUpdateService
{
    use EnsuresVehicleAttribute;

    private const EXCLUDED_FIELDS = [
        'slug',
        'uuid',
        'stock_provider',
        'vin',
        'data',
        'seo',
        'order',
        'key_features',
        'standard_spec',
        'technical_spec',
        'cap_id',
        'auto_trader_id',
        'motor_check_id',
        'new_car_id',
        'trim_id',
    ];

    private const BOOLEAN_FIELDS = [
        'published',
        'featured',
    ];

    private const INTEGER_FIELDS = [
        'engine_size_cc',
        'co2',
        'battery_range',
        'battery_capacity_kwh',
        'battery_usable_capacity_kwh',
        'battery_charge_time',
        'battery_quick_charge_time',
        'battery_quick_charge_level',
    ];

    private const DECIMAL_FIELDS = [
        'price',
        'full_price',
        'deposit',
        'mpg',
    ];

    private const DATE_FIELDS = [
        'published_at',
        'unpublished_at',
    ];

    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',
        ],
        'franchise_id' => [
            'label' => 'Franchise',
            'model' => Franchise::class,
            'label_column' => 'name',
        ],
        'fuel_type_id' => [
            'label' => 'Fuel Type',
            'model' => FuelType::class,
            'label_column' => 'name',
        ],
        'transmission_id' => [
            'label' => 'Transmission',
            'model' => TransmissionType::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',
        ],
        'type_id' => [
            'label' => 'Offer Type',
            'model' => OfferType::class,
            'label_column' => 'name',
        ],
        'form_id' => [
            'label' => 'Enquiry Form',
            'model' => Form::class,
            'label_column' => 'name',
        ],
        'test_drive_form_id' => [
            'label' => 'Test Drive Form',
            'model' => Form::class,
            'label_column' => 'name',
        ],
        'template_id' => [
            'label' => 'Template',
            'model' => Template::class,
            'label_column' => 'name',
        ],
    ];

    private const SELECT_FIELDS = [
        'vehicle_type' => [
            'label' => 'Vehicle Type',
            'options' => [
                ['value' => 'car', 'label' => 'Car'],
                ['value' => 'lcv', 'label' => 'LCV'],
                ['value' => 'motorcycle', 'label' => 'Motorcycle'],
            ],
        ],
        'new_car_type' => [
            'label' => 'New Car Type',
            'options' => [
                ['value' => 'new', 'label' => 'New'],
                ['value' => 'used', 'label' => 'Used'],
            ],
        ],
    ];

    private const FIELD_LABELS = [
        'name' => 'Name',
        'derivative' => 'Derivative',
        'price' => 'Price',
        'full_price' => 'Full Price',
        'deposit' => 'Deposit',
        'mpg' => 'MPG',
        'published' => 'Published',
        'featured' => 'Featured',
        'published_at' => 'Published At',
        'unpublished_at' => 'Unpublished At',
        'colour' => 'Colour',
        'trim' => 'Trim',
        'engine_size_cc' => 'Engine Size (cc)',
        'co2' => 'CO2',
        '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',
        'description' => 'Description',
        'excerpt' => 'Excerpt',
        'label' => 'Label',
        'youtube_video' => 'YouTube Video',
        't_and_cs' => 'Terms & Conditions',
        'cta_button1_label' => 'CTA Button 1 Label',
        'cta_button1_url' => 'CTA Button 1 URL',
        'cta_button2_label' => 'CTA Button 2 Label',
        'cta_button2_url' => 'CTA Button 2 URL',
        'footnote' => 'Footnote',
    ];

    public function getFields(): array
    {
        $offer = new VehicleOffer();
        $fillable = $offer->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', 'offer')->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 getOfferData(array $ids): array
    {
        $allowedFields = $this->getAllowedFields();
        $modelFields = array_filter($allowedFields, fn ($f) => !$this->isAttributeField($f));

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

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

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

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

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

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

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

        $offers = VehicleOffer::query()->whereIn('id', $ids)->get();

        foreach ($offers as $offer) {
            $deleted = $offer->finance()->delete();
            if ($deleted > 0) {
                $deletedCount++;
            }
        }

        return $deletedCount;
    }

    private function updateSameValue(array $ids, 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;
            }

            $offers = VehicleOffer::query()->whereIn('id', $ids)->get();
            foreach ($offers as $offer) {
                $this->updateAttributeValue($offer, $attribute, $value);
            }

            return $offers->count();
        }

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

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

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

        foreach ($updates as $offerId => $fields) {
            if (!in_array((int) $offerId, $ids)) {
                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 = VehicleOffer::query()
                    ->where('id', $offerId)
                    ->update($modelFields);
                if ($result) {
                    $updated = true;
                }
            }

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

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

        return $updatedCount;
    }

    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 (array_key_exists($field, self::SELECT_FIELDS)) {
            $config = self::SELECT_FIELDS[$field];
            return array_merge($base, [
                'type' => 'select',
                'label' => $config['label'],
                'options' => $config['options'],
            ]);
        }

        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 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 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));
    }

    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;
    }

    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(VehicleOffer $offer, VehicleAttribute $attribute, mixed $value): void
    {
        $column = $this->attributeValueColumn($attribute->toArray());

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

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