<?php

namespace App\Http\Controllers\Api;

use App\Models\User;
use App\Models\TailorSpecialization;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;

class TailorSearchController extends BaseController
{
    /**
     * Search tailors by specialization
     */
    public function searchBySpecialization(Request $request)
    {
        try {
            $query = User::where('role', 'penjahit')
                ->with('specializations');

            // Filter by specialization if provided
            if ($request->has('specialization_id') && $request->specialization_id) {
                $query->whereHas('specializations', function ($q) use ($request) {
                    $q->where('tailor_specializations.id', $request->specialization_id);
                });
            }

            // Get user's current peminatan if logged in as customer
            $userPreferred = [];
            $user = Auth::user();
            if ($user && $user->role === 'pelanggan') {
                $userPreferred = $user->preferredSpecializations->pluck('id')->toArray();
            }

            // Get tailors
            $tailors = $query->get();

            // Add distance if customer has coordinates
            if ($user && $user->role === 'pelanggan' && $user->latitude && $user->longitude) {
                foreach ($tailors as $tailor) {
                    if ($tailor->latitude && $tailor->longitude) {
                        $tailor->distance = $this->calculateDistance(
                            $user->latitude,
                            $user->longitude,
                            $tailor->latitude,
                            $tailor->longitude
                        );
                    } else {
                        $tailor->distance = null;
                    }
                }

                // Sort by distance if available
                $tailors = $tailors->sortBy('distance');
            }

            return $this->sendResponse([
                'tailors' => $tailors->values(),
                'user_preferred' => $userPreferred
            ], 'Data penjahit berhasil diambil');

        } catch (\Exception $e) {
            return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat mencari penjahit: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Get recommended tailors based on customer preferences using Content-Based Filtering
     */
    public function getRecommended()
    {
        try {
            // Log untuk debug
            \Log::info('Accessing getRecommended method');

            $user = Auth::user();

            // Check if user is customer
            if (!$user || $user->role !== 'pelanggan') {
                \Log::warning('User not authorized: ', ['user' => $user ? $user->toArray() : 'null']);
                return $this->sendError('Unauthorized.', ['error' => 'Anda harus login sebagai pelanggan'], 403);
            }

            // Get user's preferred specializations
            $preferredSpecIds = $user->preferredSpecializations->pluck('id')->toArray();
            \Log::info('User preferred specializations: ', ['preferred' => $preferredSpecIds]);

            // Get all available specializations for feature vector preparation
            $allSpecializations = TailorSpecialization::all()->pluck('id')->toArray();

            // Get all tailors
            $allTailors = User::where('role', 'penjahit')
                ->with(['specializations', 'ratings'])
                ->get();

            if (empty($preferredSpecIds)) {
                // If no preferences, return popular tailors
                \Log::info('No preferences found, returning popular tailors');
                $tailors = User::where('role', 'penjahit')
                    ->withCount([
                        'bookings' => function ($query) {
                            $query->where('status', 'selesai');
                        }
                    ])
                    ->orderBy('bookings_count', 'desc')
                    ->with(['specializations', 'ratings'])
                    ->limit(10)
                    ->get();
            } else {

                // Mengubah preferensi user menjadi vektor
                $userProfile = $this->createFeatureVector($preferredSpecIds, $allSpecializations);

                // Calculate similarity scores for all tailors
                $tailorsWithScores = [];
                foreach ($allTailors as $tailor) {
                    $tailorSpecIds = $tailor->specializations->pluck('id')->toArray();
                    $tailorProfile = $this->createFeatureVector($tailorSpecIds, $allSpecializations);

                    // Calculate cosine similarity between user and tailor profiles
                    $similarityScore = $this->calculateCosineSimilarity($userProfile, $tailorProfile);

                    // Only include tailors with some similarity
                    if ($similarityScore > 0) {
                        $tailor->similarity_score = $similarityScore;
                        $tailorsWithScores[] = $tailor;
                    }
                }

                // Sort tailors by similarity score
                $tailors = collect($tailorsWithScores)->sortByDesc('similarity_score');

                // If no similar tailors found, return popular ones
                if ($tailors->isEmpty()) {
                    $tailors = User::where('role', 'penjahit')
                        ->withCount([
                            'bookings' => function ($query) {
                                $query->where('status', 'selesai');
                            }
                        ])
                        ->orderBy('bookings_count', 'desc')
                        ->with(['specializations', 'ratings'])
                        ->limit(10)
                        ->get();
                }
            }

            \Log::info('Tailors found: ', ['count' => $tailors->count()]);

            // Add distance and rating information
            foreach ($tailors as $tailor) {
                // Calculate distance if coordinates available
                if ($user->latitude && $user->longitude && $tailor->latitude && $tailor->longitude) {
                    $tailor->distance = $this->calculateDistance(
                        $user->latitude,
                        $user->longitude,
                        $tailor->latitude,
                        $tailor->longitude
                    );
                } else {
                    $tailor->distance = null;
                }

                // Calculate average rating
                $ratings = $tailor->ratings;
                if ($ratings->count() > 0) {
                    $tailor->rating_info = [
                        'average_rating' => round($ratings->avg('rating'), 1),
                        'total_reviews' => $ratings->count(),
                        'reviews' => $ratings->map(function ($rating) {
                            return [
                                'rating' => $rating->rating,
                                'review' => $rating->review,
                                'created_at' => $rating->created_at,
                                'customer' => [
                                    'id' => $rating->customer->id,
                                    'name' => $rating->customer->name,
                                    'profile_photo' => $rating->customer->profile_photo
                                ]
                            ];
                        })
                    ];
                } else {
                    $tailor->rating_info = [
                        'average_rating' => 0,
                        'total_reviews' => 0,
                        'reviews' => []
                    ];
                }
            }

            // If user has location, use hybrid approach combining similarity and distance
            if ($user->latitude && $user->longitude) {
                // Normalize scores for hybrid ranking
                $maxDistance = $tailors->max('distance') ?: 1;
                $tailors = $tailors->map(function ($tailor) use ($maxDistance) {
                    // Calculate normalized distance score (1 when closest, 0 when furthest)
                    if ($tailor->distance !== null) {
                        $tailor->distance_score = 1 - ($tailor->distance / $maxDistance);
                    } else {
                        $tailor->distance_score = 0;
                    }

                    // Hybrid score (70% similarity, 30% proximity)
                    $tailor->hybrid_score = isset($tailor->similarity_score)
                        ? ($tailor->similarity_score * 0.7) + ($tailor->distance_score * 0.3)
                        : $tailor->distance_score;

                    return $tailor;
                })->sortByDesc('hybrid_score');
            }

            return $this->sendResponse([
                'tailors' => $tailors->values(),
                'user_preferred' => $preferredSpecIds
            ], 'Rekomendasi penjahit berhasil diambil');

        } catch (\Exception $e) {
            \Log::error('Content-Based Filtering error: ' . $e->getMessage() . "\n" . $e->getTraceAsString());
            return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat mendapatkan rekomendasi: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Create binary feature vector based on specializations
     */
    private function createFeatureVector($specializations, $allSpecializations)
    {
        $vector = [];
        foreach ($allSpecializations as $specId) {
            $vector[$specId] = in_array($specId, $specializations) ? 1 : 0;
        }
        return $vector;
    }

    /**
     * Calculate cosine similarity between two feature vectors
     */
    private function calculateCosineSimilarity($vectorA, $vectorB)
    {
        $dotProduct = 0;
        $magnitudeA = 0;
        $magnitudeB = 0;

        foreach ($vectorA as $key => $valueA) {
            $valueB = $vectorB[$key] ?? 0;

            $dotProduct += $valueA * $valueB;
            $magnitudeA += $valueA * $valueA;
            $magnitudeB += $valueB * $valueB;
        }

        $magnitudeA = sqrt($magnitudeA);
        $magnitudeB = sqrt($magnitudeB);

        if ($magnitudeA == 0 || $magnitudeB == 0) {
            return 0;
        }

        return $dotProduct / ($magnitudeA * $magnitudeB);
    }


    /**
     * Get dashboard data including nearby and recommended tailors
     */
    public function getDashboardData(Request $request)
    {
        try {
            $user = Auth::user();//Periksa apakah user yang login adalah pelanggan?
            if (!$user || !$user->isPelanggan()) {
                return $this->sendError('Unauthorized.', [], 401);
            }

            // Get nearby tailors- Mendapatkan Penjahit terdekat
            $nearbyTailors = User::where('role', 'penjahit')
                ->whereNotNull('latitude')
                ->whereNotNull('longitude')
                ->with(['specializations', 'services'])
                ->get()
                ->map(function ($tailor) use ($user) {
                    if ($user->latitude && $user->longitude && $tailor->latitude && $tailor->longitude) {
                        $tailor->distance = $this->calculateDistance(
                            $user->latitude,
                            $user->longitude,
                            $tailor->latitude,
                            $tailor->longitude
                        );
                    } else {
                        $tailor->distance = null;
                    }
                    return $tailor;
                })
                ->sortBy('distance')
                ->take(5);

            // Get recommended tailors based on user's preferences
            $preferredSpecIds = $user->preferredSpecializations()
                ->select('tailor_specializations.id')
                ->pluck('tailor_specializations.id')
                ->toArray();

            $recommendedTailors = collect([]);
            if (!empty($preferredSpecIds)) {
                $recommendedTailors = User::where('role', 'penjahit')
                    ->with(['specializations', 'services'])
                    ->whereHas('specializations', function ($query) use ($preferredSpecIds) {
                        $query->whereIn('tailor_specializations.id', $preferredSpecIds);
                    })
                    ->withCount(['bookings' => function ($query) {
                        $query->where('status', 'selesai');
                    }])
                    ->orderBy('bookings_count', 'desc')
                    ->take(5)
                    ->get()
                    ->map(function ($tailor) use ($user) {
                        if ($user->latitude && $user->longitude && $tailor->latitude && $tailor->longitude) {
                            $tailor->distance = $this->calculateDistance(
                                $user->latitude,
                                $user->longitude,
                                $tailor->latitude,
                                $tailor->longitude
                            );
                        } else {
                            $tailor->distance = null;
                        }
                        return $tailor;
                    });
            }

            return $this->sendResponse([
                'nearby_tailors' => $nearbyTailors->values(),
                'recommended_tailors' => $recommendedTailors->values(),
                'user_preferences' => $user->preferredSpecializations
            ], 'Dashboard data retrieved successfully.');

        } catch (\Exception $e) {
            \Log::error('Dashboard error: ' . $e->getMessage());
            return $this->sendError('Error retrieving dashboard data.', ['error' => $e->getMessage()], 500);
        }
    }

    /**
     * Calculate distance between two points in kilometers
     * Fungsi untuk perhitungan Jarak untuk mencari penjahit Terdekat
     */
    private function calculateDistance($lat1, $lon1, $lat2, $lon2)
    {
        $earthRadius = 6371; // Jari-jari bumi dalam kilometer

        $latDelta = deg2rad($lat2 - $lat1);
        $lonDelta = deg2rad($lon2 - $lon1);

        $a = sin($latDelta / 2) * sin($latDelta / 2) +
            cos(deg2rad($lat1)) * cos(deg2rad($lat2)) *
            sin($lonDelta / 2) * sin($lonDelta / 2);

        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));

        return round($earthRadius * $c, 2);
    }

    /**
     * Get detailed information about a tailor
     */
    public function getTailorDetail($id)
    {
        try {
            // Log untuk debug
            \Log::info('Accessing getTailorDetail method', ['id' => $id]);

            $tailor = User::where('id', $id)
                ->where('role', 'penjahit')
                ->with([
                    'specializations',
                    'services' => function($query) {
                        $query->where('is_available', true);
                    },
                    'gallery'
                ])
                ->withCount([
                    'bookings as completed_orders' => function($query) {
                        $query->where('status', 'selesai');
                    }
                ])
                ->first();

            \Log::info('Tailor query result:', ['tailor' => $tailor ? 'found' : 'not found']);

            if (!$tailor) {
                return $this->sendError('Not found.', ['error' => 'Penjahit tidak ditemukan'], 404);
            }

            // Get average rating
            $avgRating = DB::table('tailor_ratings')
                ->where('tailor_id', $id)
                ->avg('rating');

            $tailor->average_rating = round($avgRating ?? 0, 1);

            // Calculate distance if user is logged in and has coordinates
            $user = Auth::user();
            if ($user && $user->role === 'pelanggan' && $user->latitude && $user->longitude && $tailor->latitude && $tailor->longitude) {
                $tailor->distance = $this->calculateDistance(
                    $user->latitude,
                    $user->longitude,
                    $tailor->latitude,
                    $tailor->longitude
                );
            } else {
                $tailor->distance = null;
            }

            // Get gallery photos
            $gallery = \App\Models\TailorGallery::where('user_id', $id)
                ->orderBy('created_at', 'desc')
                ->get();

            // Prepare response data
            $responseData = $tailor->toArray();
            $responseData['gallery'] = $gallery;

            return $this->sendResponse($responseData, 'Detail penjahit berhasil diambil');

        } catch (\Exception $e) {
            \Log::error('Error in getTailorDetail', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat mengambil detail penjahit: ' . $e->getMessage()], 500);
        }
    }

    /**
     * Search tailors by name
     *
     * @param string $name
     * @return \Illuminate\Http\Response
     */
    public function searchByName($name)
    {
        try {
            if (!$name) {
                return $this->sendError('Error.', ['error' => 'Parameter nama diperlukan'], 400);
            }

            $query = User::where('role', 'penjahit')
                ->where('name', 'LIKE', "%{$name}%")
                ->with('specializations');

            // Get tailors
            $tailors = $query->get();

            // Add distance if customer is logged in and has coordinates
            $user = Auth::user();
            if ($user && $user->role === 'pelanggan' && $user->latitude && $user->longitude) {
                foreach ($tailors as $tailor) {
                    if ($tailor->latitude && $tailor->longitude) {
                        $tailor->distance = $this->calculateDistance(
                            $user->latitude,
                            $user->longitude,
                            $tailor->latitude,
                            $tailor->longitude
                        );
                    } else {
                        $tailor->distance = null;
                    }
                }

                // Sort by distance if available
                $tailors = $tailors->sortBy('distance');
            }

            return $this->sendResponse([
                'tailors' => $tailors->values()
            ], 'Data penjahit berhasil diambil');

        } catch (\Exception $e) {
            return $this->sendError('Error.', ['error' => 'Terjadi kesalahan saat mencari penjahit: ' . $e->getMessage()], 500);
        }
    }
}
