<?php

namespace App\Traits;

use App\Enums\StatusEnum;
use App\Models\Core\File;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Traits\Fileable;
use Illuminate\Database\Eloquent\Model;


trait ModelAction
{

    use Fileable;


    /**
     * Change a model status
     *
     * @param array $request
     * @param array $modelData
     * @return string
     */
    private function changeStatus(array $request, array $actionData): string
    {

        $response['reload'] = Arr::get($actionData, 'reload', true);
        $response['status'] = false;
        $response['message'] = trans('default.failed_to_update');


        try {
            $data = Arr::get($actionData, 'model')::where(Arr::get($actionData, 'find_by', 'uid'), Arr::get($request, 'id'))
                ->when(
                    Arr::get($actionData, 'recycle', false),
                    fn(Builder $q): Builder => $q->withTrashed()
                )->when(
                    Arr::get($actionData, 'user_id', null),
                    fn(Builder $q): Builder => $q->where('user_id', Arr::get($actionData, 'user_id'))
                )
                ->firstOrfail();
            $data->{Arr::get($request, 'column', 'status')} = Arr::get($request, 'status');
            $data->save();
            $response['status'] = true;
            $response['message'] = trans('default.updated_successfully');


        } catch (\Throwable $th) {

        }

        return json_encode($response);

    }




    protected function parseManualParameters(): array
    {

        $parameter = [];
        if (request()->has('field_name')) {
            for ($i = 0; $i < count(request()->field_name); $i++) {
                $arr = [];

                $label = @request()->field_label[$i] ?? request()->field_name[$i];

                if (@request()->instraction[$i]) {
                    $arr['instraction'] = request()->instraction[$i];
                }

                $arr['field_name'] = t2k(request()->field_name[$i]);
                $arr['field_label'] = $label;
                $arr['type'] = request()->type[$i];
                $arr['validation'] = request()->validation[$i];
                $parameter[$arr['field_name']] = $arr;
            }
        }
        return $parameter;
    }




    /**
     * Bulk action update/delete
     *
     * @param Request $request
     * @param array $actionData
     * @return array
     */
    private function bulkAction(Request $request, array $actionData): array
    {

        $type = $request->get("type");
        $response = $this->getResponse($type);
        $bulkIds = json_decode($request->input('bulk_id'), true);
        $request->merge(["bulk_id" => $bulkIds]);

        $this->validateRequest($request, $actionData);

        $bulkIds = $request->get('bulk_id');

        $model = Arr::get($actionData, 'model')::whereIn('id', $bulkIds);
        $type = $request->get("type");

        return $response;

    }




    /**
     * Validate bulk action request
     *
     * @param Request $request
     * @param array $actionData
     * @return void
     */
    public function validateRequest(Request $request, array $actionData): void
    {

        $tableName = Arr::get($actionData, 'model', null)->getTable();

        $rules = [
            'bulk_id' => ['array', 'required'],
            'bulk_id.*' => ["required", 'exists:' . $tableName . ',id'],
            'type' => ['required', Rule::in(['status', 'delete', 'restore', "force_delete", "is_feature", 'is_blocked'])],
            'value' => [
                Rule::requiredIf(fn(): bool => in_array($request->get("type"), ['status', 'is_feature', 'is_blocked'])),
                function (string $attribute, mixed $value, $fail) use ($request) {
                    if (in_array($request->get("type"), ['status', 'is_feature', 'is_blocked']) && !in_array($value, StatusEnum::toArray()))
                        $fail("The {$attribute} is invalid.");
                },
            ]
        ];

        $request->validate($rules);

    }



    /**
     * Get response
     *
     * @param string $type
     * @return array
     */
    public function getResponse(string $type): array
    {


      return response_status('Items status updated successfully');

    }


    /**
     * Force delete
     *
     * @param mixed $record
     * @param array $actionData
     * @return void
     */
    private function handleForceDelete(mixed $record, array $actionData): void
    {
        if (isset($actionData['force_flag']))
            $this->unlinkData($record, $actionData);
        $record->forceDelete();
    }



    /**
     * regular delete
     *
     * @param Model $record
     * @param array $actionData
     * @return void
     */
    private function handleDefaultDelete(Model $record, array $actionData): void
    {
        if (
            !in_array(true, array_map(
                fn(string $relation): bool =>
                $record->{$relation . "_count"} > 0
                ,
                Arr::get($actionData, 'with_count', [])
            ))
        ) {
            if (!isset($actionData['force_flag']))
                $this->unlinkData($record, $actionData);
            $record->delete();
        }
    }




    /**
     * Unlink and delete relational data
     *
     * @param Model $record
     * @param array $modelData
     * @return void
     */
    private function unlinkData(Model $record, array $modelData): void
    {

        $fileTypes = collect(Arr::get($modelData, 'file_unlink', []));
        $relations = collect(Arr::get($modelData, 'with', []));


        //unlink files
        $fileTypes->each(fn(string $path, string $type): bool =>
            $record->file()->where('type', $type)->each(fn(File $file): bool =>
                $this->unlink(location: $path, file: $file)));

        //delete data
        $relations->filter(
            fn(string $relation): bool => $relation != 'file'
        )->each(function (string $relation) use ($record): void {
            if ($relation != 'file')
                $record->{$relation}()->delete();
        });


    }




    /**
     * Save seo
     *
     * @param Model $model
     * @return void
     */
    public static function saveSeo(Model $model): void
    {


        $model->fill(Arr::collapse(Arr::map(
            ['meta_title', 'meta_description', 'meta_keywords'],
            fn(string $key): array =>
            [$key => request()->input($key)]
        )));
    }



    private function saveFile(Model $model, ?array $response = null, ?string $type = null, array $extra = []): ?File
    {
        if (is_array($response) && Arr::has($response, 'status')) {
            $file = new File(array_merge([
                'name' => Arr::get($response, 'name', 'default'),
                'disk' => Arr::get($response, 'disk', 'local'),
                'type' => $type,
                'size' => Arr::get($response, 'size', ''),
                'extension' => Arr::get($response, 'extension', ''),
            ], $extra));

            $model->file()->save($file);

            return $file;
        }

        return null;
    }



    private function saveFiles(Model $model, array $files, string $location, string $type, array $extra = []): array
    {
        $saved = [];

        foreach ($files as $file) {
            $response = $this->storeFile($file, $location);
            $saved[] = $this->saveFile($model, $response, $type, $extra);
        }

        return $saved;
    }


}
