<?php

namespace App\Http\Controllers\BloodBank;

use App\Models\BloodBank\BloodBank;
use App\Models\BloodBank\BloodInventory;
use App\Models\BloodBank\BloodRequest;
use App\Models\BloodBank\BloodDonation;
use App\Models\BloodBank\BloodDrive;
use App\Models\BloodBank\DriveRegistration;
use App\Models\User;
use App\Http\Resources\BloodBank\BloodBankResource;
use App\Http\Resources\BloodBank\BloodInventoryResource;
use App\Http\Resources\BloodBank\BloodRequestResource;
use App\Http\Resources\BloodBank\BloodDonationResource;
use App\Http\Resources\BloodBank\BloodDriveResource;
use App\Http\Resources\BloodBank\DriveRegistrationResource;
use App\Http\Resources\BloodBank\DonorResource;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Carbon\Carbon;
use Illuminate\Routing\Controller;

class BloodBankController extends Controller
{
    /**
     * Get dashboard statistics
     */
    public function getDashboardStats()
    {
        try {
            // Get blood availability summary
            $bloodAvailability = BloodInventory::selectRaw('blood_type, SUM(units_available) as total_units')
                ->groupBy('blood_type')
                ->get()
                ->keyBy('blood_type')
                ->map(function ($item) {
                    $status = 'normal';
                    if ($item->total_units <= 5) {
                        $status = 'critical';
                    } elseif ($item->total_units <= 10) {
                        $status = 'low';
                    }
                    return [
                        'units' => $item->total_units,
                        'status' => $status
                    ];
                });

            // Ensure all blood types are represented
            $bloodTypes = ['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-'];
            foreach ($bloodTypes as $type) {
                if (!isset($bloodAvailability[$type])) {
                    $bloodAvailability[$type] = [
                        'units' => 0,
                        'status' => 'critical'
                    ];
                }
            }

            // Recent blood requests
            $recentRequests = BloodRequest::with('user')
                ->where('status', '!=', 'fulfilled')
                ->where('is_public', true)
                ->where('needed_by', '>=', now())
                ->orderBy('urgency', 'desc')
                ->orderBy('created_at', 'desc')
                ->limit(5)
                ->get();

            // Get user stats if authenticated
            $userStats = null;
            if (Auth::check()) {
                $user = Auth::user();
                $userStats = [
                    'is_donor' => $user->is_donor,
                    'blood_type' => $user->blood_type,
                    'donation_count' => $user->donation_count,
                    'last_donation_date' => $user->last_donation_date ? $user->last_donation_date->format('Y-m-d') : null,
                    'days_since_last_donation' => $user->days_since_last_donation,
                    'is_eligible_to_donate' => $user->isEligibleToDonate(),
                    'eligible_date' => $user->last_donation_date ? $user->last_donation_date->addDays(56)->format('Y-m-d') : null
                ];
            }

            // Get upcoming blood drives
            $upcomingDrives = BloodDrive::where('start_datetime', '>', now())
                ->orderBy('start_datetime')
                ->limit(3)
                ->get();

            // Emergency requests
            $emergencyRequests = BloodRequest::where('urgency', 'emergency')
                ->where('status', '!=', 'fulfilled')
                ->where('is_public', true)
                ->where('needed_by', '>=', now())
                ->orderBy('created_at', 'desc')
                ->limit(1)
                ->first();

            return response()->json([
                'code' => 200,
                'message' => 'Dashboard stats retrieved successfully',
                'data' => [
                    'blood_availability' => $bloodAvailability,
                    'recent_requests' => BloodRequestResource::collection($recentRequests),
                    'emergency_request' => $emergencyRequests ? new BloodRequestResource($emergencyRequests) : null,
                    'upcoming_drives' => BloodDriveResource::collection($upcomingDrives),
                    'user_stats' => $userStats
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving dashboard stats',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get blood inventory
     */
    public function getBloodInventory()
    {
        try {
            $inventory = BloodInventory::selectRaw('blood_type, SUM(units_available) as total_units')
                ->groupBy('blood_type')
                ->get()
                ->map(function ($item) {
                    $status = 'normal';
                    if ($item->total_units <= 5) {
                        $status = 'critical';
                    } elseif ($item->total_units <= 10) {
                        $status = 'low';
                    }
                    return [
                        'blood_type' => $item->blood_type,
                        'units_available' => $item->total_units,
                        'status' => $status
                    ];
                });

            // Ensure all blood types are represented
            $bloodTypes = ['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-'];
            $result = collect($bloodTypes)->map(function ($type) use ($inventory) {
                $found = $inventory->firstWhere('blood_type', $type);
                if ($found) {
                    return $found;
                }
                return [
                    'blood_type' => $type,
                    'units_available' => 0,
                    'status' => 'critical'
                ];
            });

            return response()->json([
                'code' => 200,
                'message' => 'Blood inventory retrieved successfully',
                'data' => $result
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving blood inventory',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get blood requests
     */
    public function getBloodRequests(Request $request)
    {
        try {
            $query = BloodRequest::with('user')
                ->where('is_public', true)
                ->where('status', '!=', 'cancelled');

            // Blood type filter
            if ($request->filled('blood_type')) {
                $query->where('blood_type', $request->blood_type);
            }

            // Urgency filter
            if ($request->filled('urgency')) {
                $query->where('urgency', $request->urgency);
            }

            // Status filter
            if ($request->filled('status')) {
                $query->where('status', $request->status);
            }

            // Distance filter if user has location
            if (Auth::check() && Auth::user()->latitude && Auth::user()->longitude && $request->filled('distance')) {
                $user = Auth::user();
                $distance = $request->input('distance', 10); // Default 10km
                
                $query->selectRaw("*, (
                    6371 * acos(
                        cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + 
                        sin(radians(?)) * sin(radians(latitude))
                    )
                ) AS distance", [$user->latitude, $user->longitude, $user->latitude])
                ->having('distance', '<=', $distance)
                ->orderBy('distance');
            } else {
                // Default sorting by urgency and creation date
                $query->orderBy('urgency', 'desc')
                    ->orderBy('created_at', 'desc');
            }

            // Pagination
            $perPage = $request->input('per_page', 10);
            $requests = $query->paginate($perPage);

            return response()->json([
                'code' => 200,
                'message' => 'Blood requests retrieved successfully',
                'data' => BloodRequestResource::collection($requests),
                'meta' => [
                    'current_page' => $requests->currentPage(),
                    'last_page' => $requests->lastPage(),
                    'per_page' => $requests->perPage(),
                    'total' => $requests->total()
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving blood requests',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get blood request detail
     */
    public function getBloodRequestDetail($id)
    {
        try {
            $request = BloodRequest::with(['user', 'donations.user'])
                ->findOrFail($id);

            // Check if request is public or belongs to the authenticated user
            if (!$request->is_public && (!Auth::check() || $request->user_id !== Auth::id())) {
                return response()->json([
                    'code' => 403,
                    'message' => 'You do not have permission to view this request'
                ], 403);
            }

            return response()->json([
                'code' => 200,
                'message' => 'Blood request retrieved successfully',
                'data' => new BloodRequestResource($request)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving blood request',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Create blood request
     */
    public function createBloodRequest(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'blood_type' => 'required|string|in:A+,A-,B+,B-,AB+,AB-,O+,O-',
                'units_needed' => 'required|integer|min:1',
                'hospital_name' => 'required|string|max:255',
                'hospital_address' => 'required|string',
                'contact_phone' => 'required|string|max:20',
                'reason' => 'nullable|string',
                'urgency' => 'required|in:normal,urgent,emergency',
                'needed_by' => 'required|date|after_or_equal:today',
                'is_public' => 'boolean'
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'Validation failed',
                    'errors' => $validator->errors()
                ], 422);
            }

            $validated = $validator->validated();
            $validated['user_id'] = Auth::id();

            // Get coordinates if possible (could use a geocoding service)
            if (Auth::user()->latitude && Auth::user()->longitude) {
                $validated['latitude'] = Auth::user()->latitude;
                $validated['longitude'] = Auth::user()->longitude;
            }

            $bloodRequest = BloodRequest::create($validated);

            return response()->json([
                'code' => 201,
                'message' => 'Blood request created successfully',
                'data' => new BloodRequestResource($bloodRequest)
            ], 201);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error creating blood request',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Cancel a blood request
     */
    public function cancelRequest($id)
    {
        try {
            $request = BloodRequest::where('user_id', Auth::id())
                ->findOrFail($id);

            $request->status = 'cancelled';
            $request->save();

            return response()->json([
                'code' => 200,
                'message' => 'Blood request cancelled successfully',
                'data' => new BloodRequestResource($request)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error cancelling blood request',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Respond to a blood request
     */
    public function respondToRequest(Request $request, $id)
    {
        try {
            $bloodRequest = BloodRequest::findOrFail($id);
            
            // Validate the donation
            $validator = Validator::make($request->all(), [
                'units' => 'required|integer|min:1|max:2',
                'donation_date' => 'required|date|after_or_equal:today',
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'Validation failed',
                    'errors' => $validator->errors()
                ], 422);
            }

            $user = Auth::user();
            
            // Check blood type compatibility
            $isCompatible = $this->isBloodTypeCompatible($user->blood_type, $bloodRequest->blood_type);
            if (!$isCompatible) {
                return response()->json([
                    'code' => 422,
                    'message' => "Your blood type {$user->blood_type} is not compatible with the requested blood type {$bloodRequest->blood_type}"
                ], 422);
            }
            
            // Check eligibility
            if (!$user->isEligibleToDonate()) {
                $nextEligibleDate = $user->last_donation_date->addDays(56)->format('Y-m-d');
                return response()->json([
                    'code' => 422,
                    'message' => "You are not eligible to donate until {$nextEligibleDate}",
                ], 422);
            }

            // Create donation
            $donation = BloodDonation::create([
                'user_id' => $user->id,
                'request_id' => $id,
                'blood_bank_id' => $bloodRequest->hospital_id,
                'blood_type' => $user->blood_type,
                'units' => $request->units,
                'donation_date' => $request->donation_date,
                'status' => 'scheduled'
            ]);

            return response()->json([
                'code' => 201,
                'message' => 'Response submitted successfully',
                'data' => new BloodDonationResource($donation)
            ], 201);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error responding to request',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get nearby donors
     */
    public function getNearbyDonors(Request $request)
    {
        try {
            if (!Auth::check()) {
                return response()->json([
                    'code' => 401,
                    'message' => 'Unauthorized'
                ], 401);
            }

            $user = Auth::user();
            
            // Validate request
            $validator = Validator::make($request->all(), [
                'blood_type' => 'nullable|string|in:A+,A-,B+,B-,AB+,AB-,O+,O-',
                'distance' => 'nullable|integer|min:1|max:50'
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'Validation failed',
                    'errors' => $validator->errors()
                ], 422);
            }

            $bloodType = $request->input('blood_type');
            $distance = $request->input('distance', 10); // Default 10km
            
            $donors = $user->getNearbyDonors($bloodType, $distance);

            return response()->json([
                'code' => 200,
                'message' => 'Nearby donors retrieved successfully',
                'data' => DonorResource::collection($donors)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving nearby donors',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Schedule blood donation
     */
    public function scheduleDonation(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'blood_bank_id' => 'required|exists:blood_banks,id',
                'request_id' => 'nullable|exists:blood_requests,id',
                'donation_date' => 'required|date|after_or_equal:today',
                'units' => 'required|integer|min:1|max:2'
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'Validation failed',
                    'errors' => $validator->errors()
                ], 422);
            }

            $user = Auth::user();
            
            // Validate eligibility
            if (!$user->isEligibleToDonate()) {
                $nextEligibleDate = $user->last_donation_date->addDays(56)->format('Y-m-d');
                return response()->json([
                    'code' => 422,
                    'message' => "You are not eligible to donate until {$nextEligibleDate}",
                ], 422);
            }

            // If there's a request ID, validate blood type compatibility
            if ($request->request_id) {
                $bloodRequest = BloodRequest::findOrFail($request->request_id);
                $isCompatible = $this->isBloodTypeCompatible($user->blood_type, $bloodRequest->blood_type);
                
                if (!$isCompatible) {
                    return response()->json([
                        'code' => 422,
                        'message' => "Your blood type {$user->blood_type} is not compatible with the requested blood type {$bloodRequest->blood_type}"
                    ], 422);
                }
            }

            // Create donation
            $donation = BloodDonation::create([
                'user_id' => $user->id,
                'blood_bank_id' => $request->blood_bank_id,
                'request_id' => $request->request_id,
                'blood_type' => $user->blood_type,
                'units' => $request->units,
                'donation_date' => $request->donation_date,
                'status' => 'scheduled'
            ]);

            return response()->json([
                'code' => 201,
                'message' => 'Donation scheduled successfully',
                'data' => new BloodDonationResource($donation)
            ], 201);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error scheduling donation',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Cancel a scheduled donation
     */
    public function cancelDonation($id)
    {
        try {
            $donation = BloodDonation::where('user_id', Auth::id())
                ->where('status', 'scheduled')
                ->findOrFail($id);

            $donation->status = 'cancelled';
            $donation->save();

            return response()->json([
                'code' => 200,
                'message' => 'Donation cancelled successfully',
                'data' => new BloodDonationResource($donation)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error cancelling donation',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get upcoming blood drives
     */
    public function getBloodDrives(Request $request)
    {
        try {
            $query = BloodDrive::with('bloodBank');
            
            // Only future drives by default
            if (!$request->filled('include_past') || !$request->include_past) {
                $query->where('end_datetime', '>=', now());
            }
            
            // Distance filter if user has location
            if (Auth::check() && Auth::user()->latitude && Auth::user()->longitude && $request->filled('distance')) {
                $user = Auth::user();
                $distance = $request->input('distance', 25); // Default 25km
                
                $query->selectRaw("*, (
                    6371 * acos(
                        cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + 
                        sin(radians(?)) * sin(radians(latitude))
                    )
                ) AS distance", [$user->latitude, $user->longitude, $user->latitude])
                ->having('distance', '<=', $distance)
                ->orderBy('distance');
            } else {
                // Default order by start date
                $query->orderBy('start_datetime');
            }
            
            // Pagination
            $perPage = $request->input('per_page', 10);
            $drives = $query->paginate($perPage);

            return response()->json([
                'code' => 200,
                'message' => 'Blood drives retrieved successfully',
                'data' => BloodDriveResource::collection($drives),
                'meta' => [
                    'current_page' => $drives->currentPage(),
                    'last_page' => $drives->lastPage(),
                    'per_page' => $drives->perPage(),
                    'total' => $drives->total()
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving blood drives',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Register for blood drive
     */
    public function registerForDrive(Request $request, $driveId)
    {
        try {
            $drive = BloodDrive::findOrFail($driveId);
            
            // Check if drive is in the future
            if ($drive->start_datetime->isPast()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'This blood drive has already started'
                ], 422);
            }
            
            // Check if registration is full
            if ($drive->isRegistrationFull()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'This blood drive has reached its registration limit'
                ], 422);
            }
            
            $user = Auth::user();
            
            // Check if already registered
            $existing = DriveRegistration::where('blood_drive_id', $driveId)
                ->where('user_id', $user->id)
                ->exists();
                
            if ($existing) {
                return response()->json([
                    'code' => 422,
                    'message' => 'You are already registered for this blood drive'
                ], 422);
            }
            
            // Create registration
            $registration = DriveRegistration::create([
                'blood_drive_id' => $driveId,
                'user_id' => $user->id,
                'name' => $user->name,
                'phone' => $request->input('phone', $user->phone),
                'email' => $user->email,
                'blood_type' => $user->blood_type,
                'status' => 'registered'
            ]);
            
            return response()->json([
                'code' => 201,
                'message' => 'Successfully registered for blood drive',
                'data' => new DriveRegistrationResource($registration)
            ], 201);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error registering for blood drive',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Cancel drive registration
     */
    public function cancelDriveRegistration($driveId)
    {
        try {
            $registration = DriveRegistration::where('blood_drive_id', $driveId)
                ->where('user_id', Auth::id())
                ->where('status', 'registered')
                ->firstOrFail();
                
            $registration->status = 'cancelled';
            $registration->save();
            
            return response()->json([
                'code' => 200,
                'message' => 'Registration cancelled successfully',
                'data' => new DriveRegistrationResource($registration)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error cancelling registration',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get user's drive registrations
     */
    public function getUserDriveRegistrations()
    {
        try {
            $registrations = DriveRegistration::with(['bloodDrive.bloodBank'])
                ->where('user_id', Auth::id())
                ->orderBy('created_at', 'desc')
                ->get();
                
            return response()->json([
                'code' => 200,
                'message' => 'Drive registrations retrieved successfully',
                'data' => DriveRegistrationResource::collection($registrations)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving drive registrations',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get user's donation history
     */
    public function getUserDonations()
    {
        try {
            $donations = BloodDonation::with(['bloodBank', 'request'])
                ->where('user_id', Auth::id())
                ->orderBy('donation_date', 'desc')
                ->paginate(10);
                
            return response()->json([
                'code' => 200,
                'message' => 'Donation history retrieved successfully',
                'data' => BloodDonationResource::collection($donations),
                'meta' => [
                    'current_page' => $donations->currentPage(),
                    'last_page' => $donations->lastPage(),
                    'per_page' => $donations->perPage(),
                    'total' => $donations->total()
                ]
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error retrieving donation history',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Update user donor profile
     */
    public function updateDonorProfile(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'blood_type' => 'required|string|in:A+,A-,B+,B-,AB+,AB-,O+,O-',
                'is_donor' => 'boolean',
                'show_as_donor' => 'boolean',
                'latitude' => 'nullable|numeric',
                'longitude' => 'nullable|numeric',
            ]);

            if ($validator->fails()) {
                return response()->json([
                    'code' => 422,
                    'message' => 'Validation failed',
                    'errors' => $validator->errors()
                ], 422);
            }

            $user = Auth::user();
            $user->update($validator->validated());
            
            return response()->json([
                'code' => 200,
                'message' => 'Donor profile updated successfully',
                'data' => new DonorResource($user)
            ]);
        } catch (\Exception $e) {
            return response()->json([
                'code' => 500,
                'message' => 'Error updating donor profile',
                'error' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Helper method to check blood type compatibility
     */
    private function isBloodTypeCompatible($donorType, $recipientType)
    {
        $compatibility = [
            'O-' => ['O-', 'O+', 'A-', 'A+', 'B-', 'B+', 'AB-', 'AB+'],
            'O+' => ['O+', 'A+', 'B+', 'AB+'],
            'A-' => ['A-', 'A+', 'AB-', 'AB+'],
            'A+' => ['A+', 'AB+'],
            'B-' => ['B-', 'B+', 'AB-', 'AB+'],
            'B+' => ['B+', 'AB+'],
            'AB-' => ['AB-', 'AB+'],
            'AB+' => ['AB+']
        ];
        
        if (!isset($compatibility[$donorType])) {
            return false;
        }
        
        return in_array($recipientType, $compatibility[$donorType]);
    }
}