<?php

namespace App\Http\Controllers;

use App\AIContentRepository;
use App\Exports\OfferExport;
use App\Facades\Settings;
use App\Http\Requests\CopyOfferRequest;
use App\Http\Requests\ImportFileRequest;
use App\Http\Requests\SelectionSearchSearchRequest;
use App\Http\Requests\SetFeaturedVehicleOffersRequest;
use App\Http\Requests\StoreVehicleOfferRequest;
use App\Http\Requests\UpdateVehicleOfferRequest;
use App\Http\Resources\VehicleOfferListResource;
use App\Http\Resources\VehicleOfferViewResource;
use App\Jobs\BulkImportJob;
use App\Models\BulkImportLog;
use App\OfferRepository;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use Mtc\MercuryDataModels\VehicleModel;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\MercuryDataModels\VehicleOfferContentHistory;
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use Mtc\VehicleLookup\VehicleLookupService;

class VehicleOfferController extends Controller
{
    public function __construct()
    {
        $this->middleware([
            'permission:edit-offers',
        ]);
    }

    /**
     * Display a listing of the vehicle offers.
     *
     * @param Request $request
     * @return VehicleOfferListResource
     */
    public function index(SelectionSearchSearchRequest $request): VehicleOfferListResource
    {
        $perPage = min((int)$request->input('per_page', 15), 200);

        $offers = VehicleOffer::query()
            ->with([
                'make',
                'model',
                'mediaUses.media',
            ])
            ->withViewCount()
            ->withSearchViewCount()
            ->withImageCount()
            ->withEnquiryCount()
            ->setSortBy($request->input('sort_by', 'id_desc'))
            ->setFilters($request->input('filters') ?? '')
            ->when($request->filled('selections'), fn($query) => $query->setSelections($request->getSelections()))
            ->when(
                $request->filled('search_term'),
                fn($query) => $query->whereFullText('search_content', $request->input('search_term'))
            );

        if ($request->withArchived()) {
            $offers = $offers->withTrashed();
        }

        return new VehicleOfferListResource($offers->paginate($perPage));
    }

    /**
     * Store a newly created vehicle offer
     *
     * @param \App\Http\Requests\StoreVehicleOfferRequest $request
     * @return VehicleOfferViewResource
     */
    public function store(
        StoreVehicleOfferRequest $request,
        VehicleLookupService $lookupService,
        OfferRepository $repository
    ): VehicleOfferViewResource {
        /** @var VehicleOffer $offer */
        $offer = VehicleOffer::query()->create($request->validated());
        $offer->update([
            'vehicle_type' => VehicleModel::query()->find($request->input('model_id'))?->type
        ]);
        $repository->saveLookupData($offer, $request, $lookupService);
        return new VehicleOfferViewResource($offer->refresh());
    }

    public function show(int $offer_id): VehicleOfferViewResource
    {
        /** @var VehicleOffer $offer */
        $offer = VehicleOffer::query()->withTrashed()->findOrFail($offer_id);
        return new VehicleOfferViewResource($offer);
    }

    /**
     * Update the specified vehicle offer
     *
     * @param \App\Http\Requests\UpdateVehicleOfferRequest $request
     * @param \Mtc\MercuryDataModels\VehicleOffer $vehicleOffer
     * @return VehicleOfferViewResource
     */
    public function update(
        UpdateVehicleOfferRequest $request,
        VehicleOffer $vehicleOffer,
        VehicleLookupService $lookupService,
        OfferRepository $repository
    ): VehicleOfferViewResource {
        $repository->save($vehicleOffer, $request, Auth::id());
        $repository->saveLookupData($vehicleOffer, $request, $lookupService);

        return new VehicleOfferViewResource($vehicleOffer);
    }

    /**
     * Remove the specified vehicle offer
     *
     * @param \Mtc\MercuryDataModels\VehicleOffer $vehicleOffer
     * @return bool
     */
    public function destroy(VehicleOffer $vehicleOffer): bool
    {
        return $vehicleOffer->delete();
    }

    /**
     * Show list of featured offers
     *
     * @return VehicleOfferListResource
     */
    public function getFeatured(): VehicleOfferListResource
    {
        return new VehicleOfferListResource(
            VehicleOffer::query()
                ->with([
                    'make',
                    'model',
                    'mediaUses',
                ])
                ->where('featured', 1)
                ->get()
        );
    }

    /**
     * Set offers as featured
     *
     * @param SetFeaturedVehicleOffersRequest $request
     * @return VehicleOfferListResource
     */
    public function setFeatured(SetFeaturedVehicleOffersRequest $request): VehicleOfferListResource
    {
        VehicleOffer::query()
            ->whereNotIn('id', $request->input('offer_ids'))
            ->update([
                'featured' => 0
            ]);

        VehicleOffer::query()
            ->whereIn('id', $request->input('offer_ids'))
            ->update([
                'featured' => 1
            ]);

        return $this->getFeatured();
    }

    public function export(Request $request)
    {
        return Excel::download(new OfferExport(), 'vehicle_offers.xlsx');
    }

    /**
     * Import seo data from file
     *
     * @param ImportFileRequest $request
     * @return bool[]
     */
    public function import(ImportFileRequest $request)
    {
        $tmpName = 'offer-import-' . Carbon::now()->format('Y_m_d_H_i_s') . '.'
            . $request->file('file')->getClientOriginalExtension();
        $path_to_file = 'offer-import/' . $tmpName;

        Storage::disk('file-storage')->put($path_to_file, $request->file('file')->getContent());
        BulkImportLog::query()
            ->create([
                'user_id' => Auth::id(),
                'object_type' => 'offer',
                'filename' => $path_to_file,
            ]);
        $this->dispatch(new BulkImportJob('offer', $path_to_file, 'edit-offers'));
        return [
            'success' => true,
        ];
    }

    public function copy(
        CopyOfferRequest $request,
        VehicleOffer $vehicleOffer,
        OfferRepository $repository
    ): VehicleOfferViewResource {
        $offer = $repository->copy($vehicleOffer, $request);
        return new VehicleOfferViewResource($offer);
    }

    public function restore(int $offer_id, OfferRepository $repository): Response
    {
        $repository->restore($offer_id);
        return \response('ok');
    }

    /**
     * Sync offer content with template
     *
     * @param int $offerId
     * @param OfferRepository $repository
     * @return VehicleOfferViewResource|array
     */
    public function syncWithTemplate(int $offerId, OfferRepository $repository): VehicleOfferViewResource|array
    {
        try {
            $repository->syncContentWithTemplate($offerId);
            $offer = VehicleOffer::query()->find($offerId);
        } catch (\Exception $exception) {
            Log::error('Failed to sync offer structure ', [
                'page_id' => $offerId,
                'error' => $exception->getMessage(),
                'tenant' => tenant('id'),
            ]);

            return [
                'success' => false,
                'error' => $exception->getMessage(),
            ];
        }

        return new VehicleOfferViewResource($offer);
    }


    /**
     * @param VehicleOffer $vehicleOffer
     * @param AIContentRepository $AIContentRepository
     * @return JsonResponse
     * @throws ValidationException
     */
    public function getAIDescription(VehicleOffer $vehicleOffer, AIContentRepository $AIContentRepository): JsonResponse
    {
        if (!$AIContentRepository->hasEnabledIntegration()) {
            throw ValidationException::withMessages([
                __('ai-content.integration_not_enabled')
            ]);
        }

        $description = $AIContentRepository->getDescription($vehicleOffer);
        if (empty($description)) {
            throw ValidationException::withMessages([
                __('ai-content.description_not_available')
            ]);
        }

        return response()->json([
            'description' => $description
        ]);
    }

    public function restoreVersion(
        VehicleOffer $vehicleOffer,
        VehicleOfferContentHistory $version,
        OfferRepository $repository
    ): VehicleOfferViewResource {
        $repository->restoreVersion($vehicleOffer, $version);
        $vehicleOffer->refresh();
        return new VehicleOfferViewResource($vehicleOffer);
    }

    public function saveOrder(Request $request)
    {
        $ids = collect($request->input())->pluck('id');

        $originalOrders = VehicleOffer::whereIn('id', $ids)
            ->orderBy('order')
            ->get(['id', 'order'])
            ->pluck('order', 'id');
        try {
            DB::transaction(static function () use ($ids, $originalOrders) {
                $ids->mapWithKeys(function ($id, $index) use ($originalOrders) {
                    return [$id => $originalOrders->values()->get($index)];
                })
                    ->filter(function ($newOrderValue, $id) use ($originalOrders) {
                        return $originalOrders[$id] !== $newOrderValue;
                    })
                    ->each(function ($newOrderValue, $id) {
                        VehicleOffer::query()
                            ->where('id', $id)
                            ->update([
                                'order' => $newOrderValue,
                            ]);
                    });
            });

            return response('ok');
        } catch (\Throwable $e) {
            Log::error('Failed to save vehicle offer order', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'tenant' => tenant('id'),
            ]);

            return response()->json([
                'status' => 'error',
                'message' => 'Failed to save the new order. Please try again.',
            ], 500);
        }
    }
}
