<?php

namespace App\Http\Controllers;

use App\Facades\Settings;
use App\Http\Resources\SalesChannelList;
use App\IntegrationRepository;
use App\Jobs\DetachAutoTraderReferences;
use App\Modules\Stock\AutoTraderStock;
use App\ReportTypes\VehicleReport;
use App\SalesChannelType;
use App\Services\AutoTraderAIApi;
use App\Services\AutoTraderHub;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\SalesChannelHistory;
use Mtc\MercuryDataModels\SalesChannelRule;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAttribute;
use Mtc\MercuryDataModels\VehicleAutoTraderDerivativeMapping;

class SalesChannelController extends Controller
{
    public function index(IntegrationRepository $repository): SalesChannelList
    {
        return new SalesChannelList($repository->getEnabledForType('sales-channels'));
    }

    public function show(string $channel, IntegrationRepository $repository): array
    {
        $enabled = $repository->getEnabledForType('sales-channels');
        if (empty($enabled[$channel])) {
            abort(404);
        }

        return [
            'status' => null,
            'conditions' => $this->getConditionsForDisplay($channel),
            'data_options' => $this->getDataOptions(),
            'available_fields' => $this->getFieldTypes(),
            'name' => App::make($enabled[$channel]['class'])->name(),
            'channel' => $enabled[$channel],
            'logs' => SalesChannelHistory::query()
                ->where('channel', $channel)
                ->latest()
                ->take(50)
                ->get()
        ];
    }

    public function update(Request $request, string $channel)
    {
        SalesChannelRule::query()->updateOrCreate(
            [
                'channel' => $channel,
            ],
            [
                'conditions' => $this->getStorableConditions($request->input('conditions')),
            ]
        );
    }

    public function dealerships(string $channel): array
    {
        return Dealership::all()
            ->filter(fn (Dealership $dealership) => !empty($dealership->data['auto-trader-location-id']))
            ->groupBy(fn (Dealership $dealership) => $dealership->data['auto-trader-location-id'])
            ->map(fn ($dealerships, $locationId) => [
                'id' => $dealerships->first()->id,
                'name' => $dealerships->pluck('name')->implode(' / '),
                'location_id' => $locationId,
            ])
            ->values()
            ->toArray();
    }

    public function locationStock(string $channel, string $locationId): array
    {
        return App::make(AutoTraderStock::class)->getStockStatusForLocation($locationId);
    }

    public function updatePublishAdvert(Request $request, string $channel): array
    {
        $request->validate([
            'vehicle_id' => 'required|integer|exists:vehicles,id',
            'publish_advert' => 'required|boolean',
        ]);

        $vehicle = Vehicle::query()
            ->with([
                'dealership', 'make', 'model', 'fuelType', 'drivetrain',
                'transmission', 'bodyStyle', 'mediaUses.media', 'features',
                'equipment', 'specs', 'autoTraderData',
            ])
            ->findOrFail($request->input('vehicle_id'));

        $vehicle->autoTraderData()->updateOrCreate(
            ['vehicle_id' => $vehicle->id],
            ['publish_advert' => $request->input('publish_advert')],
        );

        $vehicle->load('autoTraderData');

        $this->exportVehicleToHub($vehicle);

        return ['success' => true];
    }

    public function detachReferences(string $channel, string $locationId): array
    {
        if ($channel !== SalesChannelType::AUTOTRADER->value) {
            abort(404);
        }

        $hasLocation = Dealership::all()
            ->contains(fn (Dealership $dealership) =>
                (string) ($dealership->data['auto-trader-location-id'] ?? '') === $locationId);

        if (!$hasLocation) {
            abort(404, 'No dealerships configured for this AutoTrader location.');
        }

        $vehicleCount = DetachAutoTraderReferences::linkedVehicleQuery($locationId)->count();

        DetachAutoTraderReferences::dispatch($locationId);

        SalesChannelHistory::query()->create([
            'channel' => $channel,
            'was_successful' => true,
            'details' => __('sales_channels.detach_references_triggered', [
                'count' => $vehicleCount,
                'location' => $locationId,
            ]),
        ]);

        return [
            'success' => true,
            'vehicle_count' => $vehicleCount,
        ];
    }

    public function removeUnmatchedVehicle(string $channel, string $locationId, string $stockId): array
    {
        $stock = App::make(AutoTraderStock::class)->getStockStatusForLocation($locationId);

        $vehicle = collect($stock['vehicles'] ?? [])
            ->first(fn ($v) => ($v['stock_id'] ?? null) === $stockId && empty($v['id']));

        if (!$vehicle) {
            abort(404, 'Vehicle not found in location stock or is linked to a local record.');
        }

        $response = App::make(AutoTraderAIApi::class)->deleteVehicle($stockId, $locationId);

        if ($response->failed()) {
            Log::warning('Failed to remove unmatched AutoTrader vehicle', [
                'location_id' => $locationId,
                'stock_id' => $stockId,
                'status' => $response->status(),
                'body' => $response->json(),
            ]);
            abort(502, 'AutoTrader rejected the removal request.');
        }

        return ['success' => true];
    }

    public function updatePublishProfile(Request $request, string $channel): array
    {
        $request->validate([
            'vehicle_id' => 'required|integer|exists:vehicles,id',
            'publish_profile' => 'required|boolean',
        ]);

        $vehicle = Vehicle::query()
            ->with([
                'dealership', 'make', 'model', 'fuelType', 'drivetrain',
                'transmission', 'bodyStyle', 'mediaUses.media', 'features',
                'equipment', 'specs', 'autoTraderData',
            ])
            ->findOrFail($request->input('vehicle_id'));

        $vehicle->autoTraderData()->updateOrCreate(
            ['vehicle_id' => $vehicle->id],
            ['publish_profile' => $request->input('publish_profile')],
        );

        $vehicle->load('autoTraderData');

        $this->exportVehicleToHub($vehicle);

        return ['success' => true];
    }

    public function updateDerivative(Request $request, string $channel): array
    {
        $request->validate([
            'vehicle_id' => 'required|integer|exists:vehicles,id',
            'derivative_id' => 'required|string',
        ]);

        $vehicle = Vehicle::query()
            ->with([
                'dealership', 'make', 'model', 'fuelType', 'drivetrain',
                'transmission', 'bodyStyle', 'mediaUses.media', 'features',
                'equipment', 'specs', 'autoTraderData',
            ])
            ->findOrFail($request->input('vehicle_id'));

        $vehicle->autoTraderData()->updateOrCreate(
            ['vehicle_id' => $vehicle->id],
            [
                'derivative_id' => $request->input('derivative_id'),
                'at_export_error' => null,
            ],
        );

        $vehicle->load('autoTraderData');

        $this->exportVehicleToHub($vehicle);

        return ['success' => true];
    }

    public function saveDerivativeMapping(Request $request, string $channel): array
    {
        $request->validate([
            'vehicle_id' => 'required|integer|exists:vehicles,id',
            'derivative_id' => 'required|string',
        ]);

        $exportRelations = [
            'dealership', 'make', 'model', 'fuelType', 'drivetrain',
            'transmission', 'bodyStyle', 'mediaUses.media', 'features',
            'equipment', 'specs', 'autoTraderData',
        ];

        $vehicle = Vehicle::query()
            ->with($exportRelations)
            ->findOrFail($request->input('vehicle_id'));

        $derivativeId = $request->input('derivative_id');

        VehicleAutoTraderDerivativeMapping::query()->updateOrCreate(
            [
                'make_id' => $vehicle->make_id,
                'model_id' => $vehicle->model_id,
                'derivative' => $vehicle->derivative ?? '',
            ],
            ['at_derivative_id' => $derivativeId],
        );

        $vehicle->autoTraderData()->updateOrCreate(
            ['vehicle_id' => $vehicle->id],
            [
                'derivative_id' => $derivativeId,
                'at_export_error' => null,
            ],
        );

        $vehicle->load('autoTraderData');

        $this->exportVehicleToHub($vehicle);

        $matching = Vehicle::query()
            ->with($exportRelations)
            ->where('make_id', $vehicle->make_id)
            ->where('model_id', $vehicle->model_id)
            ->where('derivative', $vehicle->derivative)
            ->where('id', '!=', $vehicle->id)
            ->whereDoesntHave(
                'autoTraderData',
                fn ($query) => $query->whereNotNull('derivative_id'),
            )
            ->get();

        $matching->each(function (Vehicle $other) use ($derivativeId) {
            $other->autoTraderData()->updateOrCreate(
                ['vehicle_id' => $other->id],
                [
                    'derivative_id' => $derivativeId,
                    'at_export_error' => null,
                ],
            );
            $other->load('autoTraderData');
            $this->exportVehicleToHub($other);
        });

        return [
            'success' => true,
            'applied_to_count' => $matching->count() + 1,
        ];
    }

    public function derivativeMappings(string $channel): array
    {
        $mappings = VehicleAutoTraderDerivativeMapping::query()
            ->with(['make:id,name', 'model:id,name'])
            ->orderBy('make_id')
            ->orderBy('model_id')
            ->orderBy('derivative')
            ->get()
            ->map(fn (VehicleAutoTraderDerivativeMapping $mapping) => [
                'id' => $mapping->id,
                'make' => $mapping->make?->name,
                'model' => $mapping->model?->name,
                'derivative' => $mapping->derivative,
                'at_derivative_id' => $mapping->at_derivative_id,
                'created_at' => $mapping->created_at,
                'updated_at' => $mapping->updated_at,
            ]);

        return ['mappings' => $mappings];
    }

    public function deleteDerivativeMapping(string $channel, VehicleAutoTraderDerivativeMapping $mapping): array
    {
        $mapping->delete();
        return ['success' => true];
    }

    private function exportVehicleToHub(Vehicle $vehicle): void
    {
        if (Settings::get('sales-channels-auto-trader-method') === 'ftp') {
            return;
        }

        $token = Settings::get('auto-trader-hub-token');
        if (!$token) {
            return;
        }

        try {
            App::make(AutoTraderHub::class)->setToken($token)->export($vehicle);
        } catch (\Exception $e) {
            Log::warning('Failed to export vehicle publish state to AutoTrader Hub', [
                'vehicle_id' => $vehicle->id,
                'error' => $e->getMessage(),
            ]);
        }
    }

    private function getStorableConditions(array $conditions): array
    {
        return collect($conditions)->map(function ($condition) {
            $condition['value'] = $condition['value']['operator'] == 'in'
                ? $this->convertConditionValueToArray($condition['value'])
                : $condition['value'];

            return $condition;
        })->toArray();
    }

    private function convertConditionValueToArray(array $value): array
    {
        $values = explode(',', (string) $value['value']);
        $values = collect($values)->map(fn ($value) => trim($value))->toArray();
        $value['value'] = $values;
        return $value;
    }

    private function getConditionsForDisplay(string $channel): array
    {
        $conditions = SalesChannelRule::query()->where('channel', '=', $channel)->first()?->conditions;

        return collect($conditions)->map(function ($condition) {

            if ($condition['value']['operator'] == 'in' && is_array($condition['value']['value'])) {
                $condition['value']['value'] = implode(',', $condition['value']['value']);
            }

            return $condition;
        })->toArray();
    }

    private function getDataOptions(): array
    {
        return [
            'operators' => collect(config('reports.operators', []))
                ->map(fn ($type) => [
                    'id' => $type,
                    'name' => __('reports.operators.' . $type),
                ])->values(),
        ];
    }

    private function getFieldTypes(): array
    {
        $columns = collect(App::make(VehicleReport::class)->columns())
            ->map(fn ($name, $id) => [
                'id' => $id,
                'name' => $name,
            ]);

        return $columns
            ->merge($this->getAttributeFields())
            ->sortBy('name')
            ->values()
            ->toArray();
    }

    private function getAttributeFields(): array
    {
        return VehicleAttribute::query()
            ->where('model', (new Vehicle())->getMorphClass())
            ->orderBy('name')
            ->get()
            ->map(fn (VehicleAttribute $attribute) => [
                'id' => 'attribute.' . $attribute->slug,
                'name' => $attribute->name . ' (Attribute)',
            ])
            ->toArray();
    }

    private function getCurrentStatus(string $channel): ?array
    {
        return match ($channel) {
            SalesChannelType::AUTOTRADER->value => App::make(AutoTraderStock::class)->getStockStatus(),
            default => null,
        };
    }
}
