<?php

namespace App\Services;

use App\Data\PropertyData;
use App\Data\PropertyImageData;
use App\Data\HostelData;
use App\Helpers\UrlHelper;
use App\Models\Property;
use App\Models\PropertyImage;
use App\Models\PropertyVerification;
use App\Models\Hostel;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class PropertyService
{
    /**
     * Create a new property
     */
    public function createProperty(PropertyData $propertyData, User $landlord): Property
    {
        // Check if user is a landlord
        if ($landlord->role_type !== 1) { // 1=landlord
            throw ValidationException::withMessages([
                'permission' => ['Only landlords can create properties.'],
            ]);
        }

        return DB::transaction(function () use ($propertyData, $landlord) {
            // Create the property
            $property = Property::create([
                'landlord_id' => $landlord->id,
                'property_type' => $propertyData->property_type,
                'title' => $propertyData->title,
                'description' => $propertyData->description,
                'monthly_rent' => $propertyData->monthly_rent,
                'deposit_amount' => $propertyData->deposit_amount,
                'address' => $propertyData->address,
                'latitude' => $propertyData->latitude,
                'longitude' => $propertyData->longitude,
                'bedrooms' => $propertyData->bedrooms,
                'bathrooms' => $propertyData->bathrooms,
                'size_sqm' => $propertyData->size_sqm,
                'is_furnished' => $propertyData->is_furnished,
                'status' => $propertyData->status,
                'is_verified' => $propertyData->is_verified,
            ]);

            // Create property images
            if ($propertyData->images) {
                $this->createPropertyImages($property, $propertyData->images);
            }

            // Create hostel details if property is a hostel
            if ($propertyData->property_type === 4 && $propertyData->hostel) { // 4=hostel
                $this->createHostelDetails($property, $propertyData->hostel);
            }

            return $property->load(['images', 'hostel', 'landlord']);
        });
    }

    /**
     * Update a property
     */
    public function updateProperty($propertyId, PropertyData $propertyData, User $user): ?Property
    {
        $property = Property::find((int) $propertyId);
        
        if (!$property) {
            return null;
        }

        // Check if user owns the property or is admin
        if ($property->landlord_id !== $user->id && $user->role_type !== 3) { // 3=admin
            throw ValidationException::withMessages([
                'permission' => ['You can only update your own properties.'],
            ]);
        }

        return DB::transaction(function () use ($property, $propertyData) {
            // Update the property
            $property->update($propertyData->toArray());

            // Update images if provided
            if ($propertyData->images) {
                // Delete existing images
                $property->images()->delete();
                // Create new images
                $this->createPropertyImages($property, $propertyData->images);
            }

            // Update hostel details if property is a hostel
            if ($propertyData->property_type === 4 && $propertyData->hostel) { // 4=hostel
                $this->updateHostelDetails($property, $propertyData->hostel);
            }

            return $property->fresh(['images', 'hostel', 'landlord']);
        });
    }

    /**
     * Delete a property
     */
    public function deleteProperty($propertyId, User $user): bool
    {
        $property = Property::find((int) $propertyId);
        
        if (!$property) {
            return false;
        }

        // Check if user owns the property or is admin
        if ($property->landlord_id !== $user->id && $user->role_type !== 3) { // 3=admin
            throw ValidationException::withMessages([
                'permission' => ['You can only delete your own properties.'],
            ]);
        }

        return $property->delete();
    }

    /**
     * Get properties with filtering and pagination
     */
    public function getProperties(array $filters = [], int $perPage = 15, int $page = 1): array
    {
        $query = Property::with(['images', 'hostel', 'landlord'])
            ->available()
            ->verified();

        // Apply filters
        if (isset($filters['property_type'])) {
            $query->where('property_type', $filters['property_type']);
        }

        if (isset($filters['min_price'])) {
            $query->where('monthly_rent', '>=', $filters['min_price']);
        }

        if (isset($filters['max_price'])) {
            $query->where('monthly_rent', '<=', $filters['max_price']);
        }

        if (isset($filters['bedrooms'])) {
            $query->where('bedrooms', $filters['bedrooms']);
        }

        if (isset($filters['bathrooms'])) {
            $query->where('bathrooms', $filters['bathrooms']);
        }

        if (isset($filters['is_furnished'])) {
            $query->where('is_furnished', $filters['is_furnished']);
        }

        if (isset($filters['address'])) {
            $query->where('address', 'like', '%' . $filters['address'] . '%');
        }

        if (isset($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                  ->orWhere('description', 'like', "%{$search}%")
                  ->orWhere('address', 'like', "%{$search}%");
            });
        }

        // Apply sorting
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 2; // 2=desc
        $sortOrderString = $sortOrder === 1 ? 'asc' : 'desc'; // 1=asc, 2=desc
        $query->orderBy($sortBy, $sortOrderString);

        // Get paginated results
        $properties = $query->paginate($perPage, ['*'], 'page', $page);

        // Format the properties for response
        $formattedProperties = [];
        foreach ($properties->items() as $property) {
            $formattedProperties[] = $this->formatPropertyForResponse($property);
        }

        return [
            'data' => $formattedProperties,
            'pagination' => [
                'current_page' => $properties->currentPage(),
                'per_page' => $properties->perPage(),
                'total' => $properties->total(),
                'last_page' => $properties->lastPage(),
                'from' => $properties->firstItem(),
                'to' => $properties->lastItem(),
            ]
        ];
    }

    /**
     * Get landlord's properties
     */
    public function getLandlordProperties(User $landlord, array $filters = [], int $perPage = 15, int $page = 1): array
    {
        // Start with all properties for this landlord (no status/verification filters)
        $query = Property::with(['images', 'hostel', 'landlord'])
            ->where('landlord_id', $landlord->id);

        // Apply filters only if provided
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (!empty($filters['property_type'])) {
            $query->where('property_type', $filters['property_type']);
        }

        if (!empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                  ->orWhere('description', 'like', "%{$search}%")
                  ->orWhere('address', 'like', "%{$search}%");
            });
        }

        // Apply sorting
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 2; // 2=desc
        $sortOrderString = $sortOrder === 1 ? 'asc' : 'desc'; // 1=asc, 2=desc
        $query->orderBy($sortBy, $sortOrderString);

        // Get paginated results
        $properties = $query->paginate($perPage, ['*'], 'page', $page);

        // Format the properties for response
        $formattedProperties = [];
        foreach ($properties->items() as $property) {
            $formattedProperties[] = $this->formatPropertyForResponse($property);
        }

        return [
            'data' => $formattedProperties,
            'pagination' => [
                'current_page' => $properties->currentPage(),
                'per_page' => $properties->perPage(),
                'total' => $properties->total(),
                'last_page' => $properties->lastPage(),
                'from' => $properties->firstItem(),
                'to' => $properties->lastItem(),
            ]
        ];
    }

    /**
     * Get single property details
     */
    public function getPropertyDetails($propertyId): ?Property
    {
        return Property::with(['images', 'hostel', 'landlord'])
            ->find((int) $propertyId);
    }

    /**
     * Get all properties for admin (simple method for admin controller)
     */
    public function getAllProperties(int $perPage = 15)
    {
        return Property::with(['images', 'landlord'])
            ->orderBy('created_at', 'desc')
            ->paginate($perPage);
    }

    /**
     * Get property by ID
     */
    public function getPropertyById($id)
    {
        return Property::with(['images', 'landlord', 'hostel'])->find($id);
    }

    /**
     * Create property (simple method for admin controller)
     */
    public function createPropertySimple(array $data)
    {
        return DB::transaction(function () use ($data) {
            // Extract images from data if present
            $images = $data['images'] ?? [];
            unset($data['images']);
            
            // Extract hostel data if present (before unsetting property_type)
            $hostelData = $data['hostel'] ?? null;
            $propertyType = $data['property_type'] ?? null;
            unset($data['hostel']);
            
            // Create the property
            $property = Property::create($data);
            
            // Create property images if provided
            if (!empty($images)) {
                foreach ($images as $index => $imagePath) {
                    PropertyImage::create([
                        'property_id' => $property->id,
                        'image_url' => $imagePath,
                        'is_primary' => $index === 0, // First image is primary
                    ]);
                }
            }
            
            // Create hostel details if property is a hostel and hostel data is provided
            if ($propertyType == 4 && $hostelData && !empty($hostelData['university_nearby'])) { // 4=hostel
                \Log::info('Creating hostel data', [
                    'property_id' => $property->id,
                    'hostel_data' => $hostelData
                ]);
                
                Hostel::create([
                    'property_id' => $property->id,
                    'university_nearby' => $hostelData['university_nearby'] ?? '',
                    'distance_to_university' => isset($hostelData['distance_to_university']) && $hostelData['distance_to_university'] !== '' ? (float)$hostelData['distance_to_university'] : 0,
                    'room_sharing' => isset($hostelData['room_sharing']) && $hostelData['room_sharing'] !== '' ? (int)$hostelData['room_sharing'] : 1,
                    'gender_restriction' => isset($hostelData['gender_restriction']) && $hostelData['gender_restriction'] !== '' ? (int)$hostelData['gender_restriction'] : 3,
                    'meal_plan_available' => isset($hostelData['meal_plan_available']) ? (bool)$hostelData['meal_plan_available'] : false,
                    'study_room_available' => isset($hostelData['study_room_available']) ? (bool)$hostelData['study_room_available'] : false,
                    'curfew_time' => !empty($hostelData['curfew_time']) ? $hostelData['curfew_time'] : null,
                ]);
            }
            
            return $property->load(['images', 'hostel']);
        });
    }

    /**
     * Update property (simple method for admin controller)
     */
    public function updatePropertySimple($id, array $data)
    {
        return DB::transaction(function () use ($id, $data) {
            $property = Property::find($id);
            
            if (!$property) {
                return null;
            }
            
            // Extract images from data if present
            $images = $data['images'] ?? null;
            unset($data['images']);
            
            // Extract hostel data if present (before unsetting property_type)
            $hostelData = $data['hostel'] ?? null;
            $propertyType = $data['property_type'] ?? $property->property_type;
            unset($data['hostel']);
            
            // Update the property
            $property->update($data);
            
            // Handle images if provided
            if ($images !== null && is_array($images)) {
                // Delete old images
                $property->images()->delete();
                
                // Create new images
                foreach ($images as $index => $image) {
                    // Handle both string paths and image objects
                    $imagePath = is_string($image) ? $image : ($image['image_url'] ?? null);
                    
                    if ($imagePath) {
                        PropertyImage::create([
                            'property_id' => $property->id,
                            'image_url' => $imagePath,
                            'is_primary' => $index === 0, // First image is primary
                        ]);
                    }
                }
            }
            
            // Handle hostel data if property is a hostel
            if ($propertyType == 4 && $hostelData && !empty($hostelData['university_nearby'])) { // 4=hostel
                \Log::info('Updating hostel data', [
                    'property_id' => $property->id,
                    'hostel_data' => $hostelData
                ]);
                
                $hostel = $property->hostel;
                if ($hostel) {
                    // Update existing hostel
                    $hostel->update([
                        'university_nearby' => $hostelData['university_nearby'] ?? $hostel->university_nearby,
                        'distance_to_university' => isset($hostelData['distance_to_university']) && $hostelData['distance_to_university'] !== '' ? (float)$hostelData['distance_to_university'] : $hostel->distance_to_university,
                        'room_sharing' => isset($hostelData['room_sharing']) && $hostelData['room_sharing'] !== '' ? (int)$hostelData['room_sharing'] : $hostel->room_sharing,
                        'gender_restriction' => isset($hostelData['gender_restriction']) && $hostelData['gender_restriction'] !== '' ? (int)$hostelData['gender_restriction'] : $hostel->gender_restriction,
                        'meal_plan_available' => isset($hostelData['meal_plan_available']) ? (bool)$hostelData['meal_plan_available'] : $hostel->meal_plan_available,
                        'study_room_available' => isset($hostelData['study_room_available']) ? (bool)$hostelData['study_room_available'] : $hostel->study_room_available,
                        'curfew_time' => !empty($hostelData['curfew_time']) ? $hostelData['curfew_time'] : $hostel->curfew_time,
                    ]);
                } else {
                    // Create new hostel if it doesn't exist
                    Hostel::create([
                        'property_id' => $property->id,
                        'university_nearby' => $hostelData['university_nearby'] ?? '',
                        'distance_to_university' => isset($hostelData['distance_to_university']) && $hostelData['distance_to_university'] !== '' ? (float)$hostelData['distance_to_university'] : 0,
                        'room_sharing' => isset($hostelData['room_sharing']) && $hostelData['room_sharing'] !== '' ? (int)$hostelData['room_sharing'] : 1,
                        'gender_restriction' => isset($hostelData['gender_restriction']) && $hostelData['gender_restriction'] !== '' ? (int)$hostelData['gender_restriction'] : 3,
                        'meal_plan_available' => isset($hostelData['meal_plan_available']) ? (bool)$hostelData['meal_plan_available'] : false,
                        'study_room_available' => isset($hostelData['study_room_available']) ? (bool)$hostelData['study_room_available'] : false,
                        'curfew_time' => !empty($hostelData['curfew_time']) ? $hostelData['curfew_time'] : null,
                    ]);
                }
            } elseif ($propertyType != 4 && $property->hostel) {
                // If changing from hostel to non-hostel, delete hostel record
                $property->hostel()->delete();
            }
            
            return $property->fresh(['images', 'landlord', 'hostel']);
        });
    }

    /**
     * Get all properties for admin with filtering
     */
    public function getAllPropertiesForAdmin(array $filters = [], int $perPage = 15, int $page = 1): array
    {
        // Start with all properties (no restrictions for admin)
        $query = Property::with(['images', 'hostel', 'landlord']);

        // Apply filters
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (!empty($filters['property_type'])) {
            $query->where('property_type', $filters['property_type']);
        }

        if (isset($filters['is_verified'])) {
            $query->where('is_verified', $filters['is_verified']);
        }

        if (!empty($filters['landlord_id'])) {
            $query->where('landlord_id', $filters['landlord_id']);
        }

        if (!empty($filters['search'])) {
            $search = $filters['search'];
            $query->where(function ($q) use ($search) {
                $q->where('title', 'like', "%{$search}%")
                  ->orWhere('description', 'like', "%{$search}%")
                  ->orWhere('address', 'like', "%{$search}%")
                  ->orWhereHas('landlord', function ($landlordQuery) use ($search) {
                      $landlordQuery->where('name', 'like', "%{$search}%")
                                   ->orWhere('email', 'like', "%{$search}%");
                  });
            });
        }

        // Apply sorting
        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 2; // 2=desc
        $sortOrderString = $sortOrder === 1 ? 'asc' : 'desc'; // 1=asc, 2=desc
        $query->orderBy($sortBy, $sortOrderString);

        // Get paginated results
        $properties = $query->paginate($perPage, ['*'], 'page', $page);

        // Format the properties for response
        $formattedProperties = [];
        foreach ($properties->items() as $property) {
            $formattedProperties[] = $this->formatPropertyForResponse($property);
        }

        return [
            'data' => $formattedProperties,
            'pagination' => [
                'current_page' => $properties->currentPage(),
                'per_page' => $properties->perPage(),
                'total' => $properties->total(),
                'last_page' => $properties->lastPage(),
                'from' => $properties->firstItem(),
                'to' => $properties->lastItem(),
            ]
        ];
    }

    /**
     * Verify a property (admin only)
     */
    public function verifyProperty(int $propertyId, array $data, User $admin): ?PropertyVerification
    {
        if ($admin->role_type !== 3) { // 3=admin
            throw ValidationException::withMessages([
                'permission' => ['Only administrators can verify properties.'],
            ]);
        }

        $property = Property::find($propertyId);
        
        if (!$property) {
            return null;
        }

        return DB::transaction(function () use ($property, $data, $admin) {
            $property->update(['is_verified' => true]);

            $verification = PropertyVerification::create([
                'property_id' => $property->id,
                'verified_by' => $admin->id,
                'verification_date' => now(),
                'documents_verified' => $data['documents_verified'] ?? null,
                'notes' => $data['notes'] ?? null,
            ]);

            return $verification->load(['verifier', 'property']);
        });
    }

    /**
     * Update property status (admin only)
     */
    public function updatePropertyStatus(int $propertyId, int $status, User $admin): ?Property
    {
        if ($admin->role_type !== 3) { // 3=admin
            throw ValidationException::withMessages([
                'permission' => ['Only administrators can update property status.'],
            ]);
        }

        if (!in_array($status, [1, 2, 3, 4])) { // 1=available, 2=occupied, 3=pending, 4=maintenance
            throw ValidationException::withMessages([
                'status' => ['Invalid status provided.'],
            ]);
        }

        $property = Property::find($propertyId);
        
        if (!$property) {
            return null;
        }

        $property->update(['status' => $status]);
        
        return $property->fresh(['images', 'hostel', 'landlord']);
    }

    /**
     * Create property images
     */
    private function createPropertyImages(Property $property, array $images): void
    {
        foreach ($images as $imageData) {
            PropertyImage::create([
                'property_id' => $property->id,
                'image_url' => $imageData['image_url'],
                'is_primary' => $imageData['is_primary'] ?? false,
            ]);
        }
    }

    /**
     * Create hostel details
     */
    private function createHostelDetails(Property $property, HostelData $hostelData): void
    {
        Hostel::create([
            'property_id' => $property->id,
            'university_nearby' => $hostelData->university_nearby,
            'distance_to_university' => $hostelData->distance_to_university,
            'room_sharing' => $hostelData->room_sharing,
            'gender_restriction' => $hostelData->gender_restriction,
            'meal_plan_available' => $hostelData->meal_plan_available,
            'study_room_available' => $hostelData->study_room_available,
            'curfew_time' => $hostelData->curfew_time,
        ]);
    }

    /**
     * Update hostel details
     */
    private function updateHostelDetails(Property $property, HostelData $hostelData): void
    {
        $hostel = $property->hostel;
        
        if ($hostel) {
            $hostel->update($hostelData->toArray());
        } else {
            $this->createHostelDetails($property, $hostelData);
        }
    }

    /**
     * Format property for API response
     */
    public function formatPropertyForResponse(Property $property, array $additionalData = []): array
    {
        $propertyData = [
            'id' => $property->id,
            'property_type' => $property->property_type,
            'property_type_name' => $property->property_type_name,
            'title' => $property->title,
            'description' => $property->description,
            'monthly_rent' => $property->monthly_rent,
            'deposit_amount' => $property->deposit_amount,
            'address' => $property->address,
            'latitude' => $property->latitude,
            'longitude' => $property->longitude,
            'bedrooms' => $property->bedrooms,
            'bathrooms' => $property->bathrooms,
            'size_sqm' => $property->size_sqm,
            'is_furnished' => $property->is_furnished,
            'status' => $property->status,
            'status_name' => $property->status_name,
            'is_verified' => $property->is_verified,
            'created_at' => $property->created_at,
            'updated_at' => $property->updated_at,
            'landlord' => [
                'id' => $property->landlord->id,
                'name' => $property->landlord->name,
                'email' => $property->landlord->email,
                'phone' => $property->landlord->phone,
            ],
            'images' => $property->images->map(fn($img) => [
                'id' => $img->id,
                'image_url' => UrlHelper::toFullUrl($img->image_url),
                'is_primary' => $img->is_primary,
            ]),
            'hostel' => $property->hostel ? [
                'university_nearby' => $property->hostel->university_nearby,
                'distance_to_university' => $property->hostel->distance_to_university,
                'room_sharing' => $property->hostel->room_sharing,
                'room_sharing_name' => $property->hostel->room_sharing_name,
                'gender_restriction' => $property->hostel->gender_restriction,
                'gender_restriction_name' => $property->hostel->gender_restriction_name,
                'meal_plan_available' => $property->hostel->meal_plan_available,
                'study_room_available' => $property->hostel->study_room_available,
                'curfew_time' => $property->hostel->curfew_time?->format('H:i'),
            ] : null,
        ];

        return array_merge(['property' => $propertyData], $additionalData);
    }


    /**
     * Get property verifications
     */
    public function getPropertyVerifications(int $propertyId, array $filters = [], int $perPage = 15): array
    {
        $query = PropertyVerification::forProperty($propertyId)->with(['verifier', 'property']);

        // Apply filters
        if (isset($filters['verifier_id'])) {
            $query->byVerifier($filters['verifier_id']);
        }

        if (isset($filters['date_from']) && isset($filters['date_to'])) {
            $query->dateRange($filters['date_from'], $filters['date_to']);
        }

        if (isset($filters['recent']) && $filters['recent']) {
            $query->recent($filters['limit'] ?? 10);
        }

        $verifications = $query->orderBy('verification_date', 'desc')->paginate($perPage);

        return [
            'data' => $verifications->items(),
            'pagination' => [
                'current_page' => $verifications->currentPage(),
                'per_page' => $verifications->perPage(),
                'total' => $verifications->total(),
                'last_page' => $verifications->lastPage(),
                'from' => $verifications->firstItem(),
                'to' => $verifications->lastItem(),
            ]
        ];
    }

    /**
     * Get all verifications (admin only)
     */
    public function getAllVerifications(array $filters = [], int $perPage = 15): array
    {
        $query = PropertyVerification::with(['verifier', 'property']);

        // Apply filters
        if (isset($filters['verifier_id'])) {
            $query->byVerifier($filters['verifier_id']);
        }

        if (isset($filters['date_from']) && isset($filters['date_to'])) {
            $query->dateRange($filters['date_from'], $filters['date_to']);
        }

        if (isset($filters['recent']) && $filters['recent']) {
            $query->recent($filters['limit'] ?? 10);
        }

        $verifications = $query->orderBy('verification_date', 'desc')->paginate($perPage);

        return [
            'data' => $verifications->items(),
            'pagination' => [
                'current_page' => $verifications->currentPage(),
                'per_page' => $verifications->perPage(),
                'total' => $verifications->total(),
                'last_page' => $verifications->lastPage(),
                'from' => $verifications->firstItem(),
                'to' => $verifications->lastItem(),
            ]
        ];
    }

    /**
     * Get verification details
     */
    public function getVerificationDetails(int $verificationId): ?array
    {
        $verification = PropertyVerification::with(['verifier', 'property'])->find($verificationId);
        
        if (!$verification) {
            return null;
        }

        return $this->formatVerificationForResponse($verification);
    }

    /**
     * Get verification statistics
     */
    public function getVerificationStatistics(): array
    {
        return [
            'total_verifications' => PropertyVerification::count(),
            'verified_properties' => Property::where('is_verified', true)->count(),
            'unverified_properties' => Property::where('is_verified', false)->count(),
            'verifications_this_month' => PropertyVerification::whereMonth('verification_date', now()->month)->count(),
            'verifications_by_verifier' => PropertyVerification::with('verifier')
                ->get()
                ->groupBy('verified_by')
                ->map(function ($verifications) {
                    return $verifications->count();
                }),
        ];
    }

    /**
     * Format verification for API response
     */
    public function formatVerificationForResponse(PropertyVerification $verification): array
    {
        return [
            'id' => $verification->id,
            'property_id' => $verification->property_id,
            'property' => [
                'id' => $verification->property->id,
                'title' => $verification->property->title,
                'address' => $verification->property->address,
            ],
            'verified_by' => [
                'id' => $verification->verifier->id,
                'name' => $verification->verifier->name,
                'email' => $verification->verifier->email,
            ],
            'verification_date' => $verification->verification_date,
            'documents_verified' => $verification->documents_verified,
            'notes' => $verification->notes,
            'created_at' => $verification->created_at,
            'updated_at' => $verification->updated_at,
        ];
    }
}
