<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Requests\Expert\StoreExpertProfileRequest;
use App\Http\Resources\ExpertResource;
use App\Models\ConsultationRequest;
use App\Models\Expert;
use App\Models\Rating;
use App\Models\User;
use App\Support\ApiResponse;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;

class ExpertController extends Controller
{
    public function index(Request $request)
    {
        Gate::authorize('viewAny', Expert::class);

        $query = Expert::query()
            ->with(['user', 'province', 'county'])
            ->withCount([
                'consultationRequests as consultations_count',
            ]);

        if (! $request->user()?->hasRole('admin')) {
            $query->whereIn('status', [Expert::STATUS_APPROVED, 'active']);
        } elseif ($request->filled('status')) {
            $query->where('status', $request->string('status'));
        }

        $search = $request->input('q', $request->input('search'));
        if (filled($search)) {
            $query->where(function ($q) use ($search): void {
                $q->where('brand_name', 'like', "%{$search}%")
                    ->orWhere('main_category', 'like', "%{$search}%")
                    ->orWhere('workplace', 'like', "%{$search}%")
                    ->orWhereHas('user', function ($uq) use ($search): void {
                        $uq->where('first_name', 'like', "%{$search}%")
                            ->orWhere('last_name', 'like', "%{$search}%")
                            ->orWhere('cellphone', 'like', "%{$search}%");
                    });
            });
        }

        if ($request->filled('province_id')) {
            $query->where('work_province_id', $request->integer('province_id'));
        }

        if ($request->filled('county_id')) {
            $query->where('work_county_id', $request->integer('county_id'));
        }

        if ($request->filled('main_category')) {
            $query->where('main_category', 'like', '%' . $request->string('main_category') . '%');
        }

        $perPage = min($request->integer('per_page', 15), 50);

        return ExpertResource::collection(
            $request->boolean('noPaginate') ? $query->get() : $query->paginate($perPage)
        );
    }

    public function store(StoreExpertProfileRequest $request)
    {
        Gate::authorize('create', Expert::class);

        try {
            DB::beginTransaction();

            $user = $this->resolveOrCreateUser($request);

            if ($user->expert) {
                DB::rollBack();

                return ApiResponse::badRequest('برای این کاربر قبلاً پروفایل خدمات‌دهنده ثبت شده است.');
            }

            $data = $this->expertPayload($request);
            $data['user_id'] = $user->id;
            $data['created_by'] = Auth::id() ?? $user->id;
            $data['status'] = Expert::STATUS_PENDING;

            $expert = Expert::create($data);
            $user->syncRoles([User::ROLE_EXPERT, User::ROLE_USER]);
            $user->update(['account_type' => User::ACCOUNT_EXPERT]);

            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 419);
        }

        return ApiResponse::created(
            new ExpertResource($expert->load(['user', 'province', 'county'])),
            $expert->status === Expert::STATUS_APPROVED
                ? 'خدمات‌دهنده ثبت شد.'
                : 'درخواست خدمات‌دهنده ثبت شد و در انتظار تایید ادمین است.'
        );
    }

    public function show(Expert $expert)
    {
        Gate::authorize('view', $expert);

        $expert->load([
            'user',
            'province',
            'county',
            'educationItems',
            'certificates',
        ])->loadCount([
            'consultationRequests as consultations_count',
        ]);

        $satisfactionAvg = Rating::query()
            ->where('rateable_type', Expert::class)
            ->where('rateable_id', $expert->id)
            ->avg('score');

        $expert->setAttribute('satisfaction_avg', $satisfactionAvg !== null ? round((float) $satisfactionAvg, 1) : null);
        $expert->setAttribute(
            'projects_count',
            is_array($expert->work_experience) ? count($expert->work_experience) : 0
        );
        $expert->setAttribute('featured_projects', $this->featuredProjects($expert));

        return new ExpertResource($expert);
    }

    public function update(StoreExpertProfileRequest $request, Expert $expert)
    {
        Gate::authorize('update', $expert);

        if (
            ! $request->user()->hasRole('admin')
            && ! in_array($expert->status, [Expert::STATUS_PENDING, Expert::STATUS_NEEDS_EDIT, Expert::STATUS_REJECTED], true)
        ) {
            return ApiResponse::forbidden('این پروفایل قابل ویرایش نیست.');
        }

        try {
            DB::beginTransaction();

            if ($expert->user) {
                $fields = $request->only([
                    'first_name', 'last_name', 'avatar', 'national_code', 'national_cart_image',
                    'email', 'phone', 'dob', 'address', 'province_id', 'county_id', 'postal_code',
                ]);
                $expert->user->update(array_filter($fields, fn ($v) => $v !== null));
            }

            $data = $this->expertPayload($request);
            $data['updated_by'] = Auth::id();

            if (! $request->user()->hasRole('admin') && $expert->status === Expert::STATUS_NEEDS_EDIT) {
                $data['status'] = Expert::STATUS_PENDING;
                $data['admin_reason'] = null;
            }

            $expert->update($data);
            DB::commit();
        } catch (Exception $exception) {
            DB::rollBack();

            return ApiResponse::response(message: $exception->getMessage(), status: 419);
        }

        return new ExpertResource($expert->fresh()->load(['user', 'province', 'county']));
    }

    public function destroy(Expert $expert)
    {
        Gate::authorize('delete', $expert);
        $expert->delete();

        return ApiResponse::success(message: 'با موفقیت حذف شد');
    }

    private function resolveOrCreateUser(StoreExpertProfileRequest $request): User
    {
        if ($request->user()) {
            return $request->user();
        }

        abort(401, 'برای ثبت پروفایل خدمات‌دهنده باید وارد شوید.');
    }

    private function expertPayload(StoreExpertProfileRequest $request): array
    {
        return $request->safe()->only([
            'brand_name', 'position', 'workplace', 'main_category', 'sub_category', 'species_type',
            'products', 'work_email', 'work_phone', 'work_province_id', 'work_county_id',
            'work_address', 'work_postal_code', 'work_latitude', 'work_longitude',
            'business_license', 'education_certificates', 'skill_certificates', 'work_experience',
        ]);
    }

    /**
     * @return list<array<string, mixed>>
     */
    private function featuredProjects(Expert $expert): array
    {
        $experience = $expert->work_experience;
        if (! is_array($experience) || $experience === []) {
            return [];
        }

        $items = array_values(array_filter($experience, function ($item) {
            if (! is_array($item)) {
                return false;
            }

            return ($item['featured'] ?? false) === true
                || ($item['is_featured'] ?? false) === true
                || ($item['highlight'] ?? false) === true;
        }));

        if ($items === []) {
            $items = array_slice($experience, 0, 5);
        }

        return array_map(function ($item) {
            if (! is_array($item)) {
                return ['title' => (string) $item];
            }

            return [
                'title' => $item['title'] ?? $item['name'] ?? $item['project'] ?? null,
                'description' => $item['description'] ?? $item['desc'] ?? null,
                'year' => $item['year'] ?? null,
                'role' => $item['role'] ?? null,
            ];
        }, $items);
    }
}
