<?php

namespace App\Services;

use App\Models\Property;
use App\Models\FloorPlan;
use App\Models\Unit;
use App\Models\SyncLog;
use App\Models\ResmanAccount;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cache;
use finfo;

class ResManService
{
    private $apiHits = 0;

    /**
     * Run the Sync for multiple property IDs
     */
    public function sync(array $propertyIds, $accountId = null, $syncType = 'manual')
    {
        $totalSynced = 0;
        $imagesProcessed = 0;
        $this->apiHits = 0;
        $totalToSync = count($propertyIds);

        if ($accountId) {
            Cache::put("sync_batch_progress_$accountId", [
                'total' => $totalToSync,
                'current' => 0,
                'status' => 'Syncing properties...',
                'completed' => false
            ], 3600);
        }

        // Group properties by account to avoid repetitive account loading
        $properties = Property::whereIn('property_id', $propertyIds)
            ->where('is_status', 1)
            ->get()
            ->groupBy('resman_account_id');

        $currentCount = 0;
        foreach ($properties as $accId => $props) {
            $account = $accId ? ResmanAccount::find($accId) : null;

            foreach ($props as $property) {
                $currentCount++;
                if ($accountId) {
                    Cache::put("sync_batch_progress_$accountId", [
                        'total' => $totalToSync,
                        'current' => $currentCount,
                        'status' => "Processing $currentCount of $totalToSync: {$property->name}",
                        'completed' => false
                    ], 3600);
                }

                try {
                    $result = $this->syncProperty($property->property_id, $account, $syncType);
                    if ($result['status'] === 'success') {
                        $totalSynced++;
                        $imagesProcessed += $result['images_processed'];
                    } else {
                        Log::error("Sync failed for property {$property->property_id}: " . ($result['message'] ?? 'Unknown error'));
                    }
                } catch (\Exception $e) {
                    Log::error("Critical error syncing property {$property->property_id}: " . $e->getMessage());
                    // Continue to next property
                }
            }
        }

        if ($accountId) {
            Cache::put("sync_batch_progress_$accountId", [
                'total' => $totalToSync,
                'current' => $totalToSync,
                'status' => 'Finalizing synchronization...',
                'completed' => true
            ], 3600);
        }

        // Global Final Log
        SyncLog::create([
            'status' => 'success',
            'message' => 'Batch sync completed',
            'properties_synced' => $totalSynced,
            'images_processed' => $imagesProcessed,
            'api_hits' => $this->apiHits,
            'sync_type' => $syncType
        ]);

        return [
            'total_synced' => $totalSynced,
            'images_processed' => $imagesProcessed,
            'api_hits' => $this->apiHits
        ];
    }

    /**
     * Sync a single property
     */
    public function syncProperty($propId, $account = null, $syncType = 'manual')
    {
        @set_time_limit(0);
        @ini_set('memory_limit', '512M');

        $hitsStart = $this->apiHits;
        $imagesProcessed = 0;

        // Resolve account if not provided
        if (!$account) {
            $propertyRecord = Property::where('property_id', $propId)->first();
            if ($propertyRecord && $propertyRecord->resman_account_id) {
                $account = ResmanAccount::find($propertyRecord->resman_account_id);
            }
        }

        // Credentials resolution logic (Account model -> Config Fallback)
        $partnerId = $account->partner_id ?? config('resman.partner_id');
        $apiKey = $account->api_key ?? config('resman.api_key');
        $resmanAccId = $account->account_id ?? config('resman.account_id');
        $subdomain = $account->subdomain ?? config('resman.subdomain');
        $marketingUrl = $account->marketing_url ?? config('resman.marketing_url');

        $params = [
            'IntegrationPartnerID' => $partnerId,
            'ApiKey' => $apiKey,
            'AccountID' => $resmanAccId,
            'PropertyID' => $propId,
            'includeConcessionsInLeaseTermPricing' => 'true',
            'IncludeFullPricingMatrix' => 'true',
        ];

        try {
            Cache::put("sync_progress_$propId", "Step 1: Connecting to ResMan API...", 300);
            $response = Http::asForm()->connectTimeout(60)->timeout(180)->post($marketingUrl, $params);
            $this->apiHits++;

            if (!$response->successful()) {
                Log::error("API Call Failed for $propId: Status " . $response->status() . " Body: " . $response->body());
                return ['status' => 'error', 'message' => 'API Call Failed with status ' . $response->status()];
            }

            $responseBody = $response->body();
            Log::info("ResMan Raw Response for $propId: " . substr($responseBody, 0, 2000));
            $data = json_decode($responseBody, true);

            if (json_last_error() !== JSON_ERROR_NONE || (strpos($responseBody, '<?xml') !== false)) {
                $responseBody = str_replace("MITS:", "", $responseBody);
                $xml = @simplexml_load_string($responseBody);
                if ($xml) {
                    $data = json_decode(json_encode($xml), true);
                }
            }

            if (!$data) {
                return ['status' => 'error', 'message' => 'Failed to parse API response'];
            }

            $propertyData = $data['PhysicalProperty'] ?? $data['Response']['PhysicalProperty'] ?? null;
            if (isset($propertyData['Property'])) {
                $propertyData = $propertyData['Property'];
            }

            if (!$propertyData) {
                // Try to extract a specific error message from ResMan if available
                $description = $data['Response']['Transaction']['ResponseStatus']['Description']
                    ?? $data['Transaction']['ResponseStatus']['Description']
                    ?? null;

                $errorMsg = $description ? "ResMan API Error: $description" : "Property ID #$propId does not match this ResMan account or is invalid.";

                Cache::put("sync_progress_$propId", "Error: $errorMsg", 300);
                return ['status' => 'error', 'message' => $errorMsg];
            }

            Cache::put("sync_progress_$propId", "Step 2: API response received, starting storage...", 300);

            $responseData = $data['Response'] ?? $data;

            // Extract Special Offer
            $specialOfferTitle = '';
            $specialOfferContent = '';
            if (isset($propertyData['Concession'])) {
                $concession = isset($propertyData['Concession'][0]) ? $propertyData['Concession'][0] : $propertyData['Concession'];
                $specialOfferTitle = $this->getVal($concession['DescriptionHeader'] ?? '');
                $specialOfferContent = $this->getVal($concession['DescriptionBody'] ?? '');
            }

            // 1. Save Property
            $property = Property::updateOrCreate(
                ['property_id' => $propId],
                [
                    'resman_account_id' => $account ? $account->id : null,
                    'name' => $this->getVal($propertyData['PropertyID']['Identification']['MarketingName'] ?? $propertyData['PropertyID']['MarketingName'] ?? $responseData['MarketingName'] ?? 'Unknown Property'),
                    'address' => $this->getVal($propertyData['PropertyID']['Address']['Address1'] ?? $propertyData['Address']['Address1'] ?? ''),
                    'city' => $this->getVal($propertyData['PropertyID']['Address']['City'] ?? $propertyData['Address']['City'] ?? ''),
                    'state' => $this->getVal($propertyData['PropertyID']['Address']['State'] ?? $propertyData['Address']['State'] ?? ''),
                    'zip' => $this->getVal($propertyData['PropertyID']['Address']['PostalCode'] ?? $propertyData['Address']['PostalCode'] ?? ''),
                    'special_offer_title' => $specialOfferTitle,
                    'special_offer_content' => $specialOfferContent,
                    'url_availability' => 'https://' . $subdomain . '/Portal/Applicants/Availability?a=' . $resmanAccId . '&p=' . $propId,
                    'url_guest_card' => 'https://' . $subdomain . '/Portal/Prospects/NewGuestCard?a=' . $resmanAccId . '&p=' . $propId,
                    'url_registration' => 'https://' . $subdomain . '/Portal/Access/ApplicantRegistration?accountID=' . $resmanAccId . '&propertyID=' . $propId,
                    'api_hits' => ($this->apiHits - $hitsStart),
                    'last_sync' => now()
                ]
            );

            Cache::put("sync_progress_$propId", "Step 3: Property details stored in database.", 300);

            // Update stats on property model
            $property->increment('sync_count');
            $property->increment('total_api_hits', ($this->apiHits - $hitsStart));

            if ($syncType === 'auto') {
                $property->increment('auto_sync_hits', ($this->apiHits - $hitsStart));
            } else {
                $property->increment('manual_sync_hits', ($this->apiHits - $hitsStart));
            }

            // 2. Process Floorplans
            Cache::put("sync_progress_$propId", "Step 4: Fetching floor plans...", 300);
            $floorplans = $propertyData['Floorplan'] ?? [];
            if (isset($floorplans['Name'])) {
                $floorplans = [$floorplans];
            }

            $ilsUnitsRaw = $propertyData['ILS_Unit'] ?? [];
            $ilsUnits = [];
            if (!empty($ilsUnitsRaw)) {
                if (isset($ilsUnitsRaw['Unit'])) {
                    $ilsUnits = isset($ilsUnitsRaw['Unit'][0]) ? $ilsUnitsRaw['Unit'] : [$ilsUnitsRaw['Unit']];
                } elseif (isset($ilsUnitsRaw[0])) {
                    $ilsUnits = $ilsUnitsRaw;
                } else {
                    $ilsUnits = [$ilsUnitsRaw];
                }
            }

            $fpCount = count($floorplans);
            $fpIndex = 0;
            $syncedFloorplanIds = [];

            // Build floor-plan pricing ranges from the lease-term matrix returned by ResMan.
            // Do not infer a property-wide add-on from one unit; fees vary by unit/property.
            $floorplanPriceRanges = [];
            foreach ($ilsUnits as $priceUnit) {
                $nested = $priceUnit['Units']['Unit'] ?? [];
                if (isset($nested[0])) $nested = $nested[0];
                $unitInfoForPrice = array_merge($priceUnit, is_array($nested) ? $nested : []);
                $priceFloorplanName = trim((string) $this->getVal($unitInfoForPrice['FloorplanName'] ?? ''));
                $priceFloorplanType = trim((string) $this->getVal($unitInfoForPrice['UnitType'] ?? ''));
                $termPrices = $this->extractLeaseTermPrices($priceUnit, 'EffectiveRent');
                if (empty($termPrices)) $termPrices = $this->extractLeaseTermPrices($priceUnit, 'Rent');
                if (empty($termPrices)) $termPrices = $this->extractLeaseTermPrices($priceUnit, 'MarketRent');
                if (!$termPrices) continue;

                foreach (array_filter([$priceFloorplanName, $priceFloorplanType]) as $priceKey) {
                    if (!isset($floorplanPriceRanges[$priceKey])) $floorplanPriceRanges[$priceKey] = [];
                    $floorplanPriceRanges[$priceKey] = array_merge($floorplanPriceRanges[$priceKey], $termPrices);
                    $totalTermPrices = $this->extractLeaseTermPrices($priceUnit, 'TotalRent');
                    if ($totalTermPrices) {
                        $totalKey = $priceKey . ':TotalRent';
                        if (!isset($floorplanPriceRanges[$totalKey])) $floorplanPriceRanges[$totalKey] = [];
                        $floorplanPriceRanges[$totalKey] = array_merge($floorplanPriceRanges[$totalKey], $totalTermPrices);
                    }
                }
            }

            foreach ($floorplans as $fp) {
                $fpIndex++;
                Cache::put("sync_progress_$propId", "Step 5: Processing floorplan $fpIndex of $fpCount (images & webp)...", 300);
                $fpId = $fp['IDValue'] ?? $fp['@attributes']['IDValue'] ?? ($propId . '_' . ($fp['Name'] ?? uniqid()));

                // Extract image URL
                $imageUrl = null;
                $files = $fp['File'] ?? [];
                if (isset($files['Src'])) {
                    $files = [$files];
                }
                foreach ($files as $file) {
                    $type = $file['@attributes']['FileType'] ?? $file['FileType'] ?? $file['Type'] ?? '';
                    if ($type === 'Floorplan' || empty($type)) {
                        $imageUrl = $file['Src'] ?? null;
                        break;
                    }
                }

                $localPath = $this->processImage($imageUrl, md5($fpId), $propId);
                if ($localPath) $imagesProcessed++;

                // Portal rent range is driven by the available unit lease-term pricing matrix.
                $fpNameForPricing = trim((string) $this->getVal($fp['Name'] ?? ''));
                $fpPriceKey = $fpId;
                $fpPrices = $floorplanPriceRanges[$fpNameForPricing] ?? $floorplanPriceRanges[$fpPriceKey] ?? [];

                if ($fpPrices) {
                    $rentMin = (float) min($fpPrices);
                    $rentMax = (float) max($fpPrices);
                } else {
                    // Fallback only when the API returned no unit-level term matrix.
                    $rentMin = (float) $this->getVal($fp['MarketRent'] ?? $fp['Rent-Min'] ?? 0, 'Min');
                    $rentMax = (float) $this->getVal($fp['MarketRent'] ?? $fp['Rent-Max'] ?? 0, 'Max');
                    if ($rentMin == 0) $rentMin = (float) $this->getVal($fp['EffectiveRent'] ?? 0, 'Min');
                    if ($rentMax == 0) $rentMax = (float) $this->getVal($fp['EffectiveRent'] ?? 0, 'Max');
                }

                // Keep Total Rent fields separate; never add an inferred fee to Effective Rent.
                $trPrices = $floorplanPriceRanges[$fpNameForPricing . ':TotalRent'] ?? [];
                $trMin = $trPrices ? (float) min($trPrices) : $rentMin;
                $trMax = $trPrices ? (float) max($trPrices) : $rentMax;
                $totalRent = $trMax;

                // We still loop units to update individual unit total_rents
                foreach ($ilsUnits as $unit) {
                    $uInfo = $unit['Units']['Unit'] ?? [];
                    if (isset($uInfo[0])) $uInfo = $uInfo[0];

                    $uType = $uInfo['UnitType'] ?? '';
                    $uFpName = trim($uInfo['FloorplanName'] ?? '');

                    // (Just used to ensure we match the right units during sync)
                }

                $depositRaw = $fp['Deposit']['Amount']['ValueRange']['@attributes']['Exact'] ?? $this->getVal($fp['Deposit'] ?? '');
                $deposit = number_format((float) preg_replace('/[^0-9.]/', '', $depositRaw), 2, '.', '');

                $fpModel = FloorPlan::updateOrCreate(
                    ['property_id' => $propId, 'floorplan_id' => $fpId],
                    [
                        'name' => $this->getVal($fp['Name'] ?? 'Unnamed'),
                        'beds' => (float) $this->getVal($fp['Room'][0]['Count'] ?? $fp['Bedroom'] ?? 0),
                        'baths' => (float) $this->getVal($fp['Room'][1]['Count'] ?? $fp['Bathroom'] ?? 0),
                        'sqft_min' => (int) $this->getVal($fp['SquareFeet']['Min'] ?? $fp['SquareFeet'] ?? 0, 'Min'),
                        'sqft_max' => (int) $this->getVal($fp['SquareFeet']['Max'] ?? $fp['SquareFeet'] ?? 0, 'Max'),
                        'rent_min' => $rentMin,
                        'rent_max' => $rentMax,
                        'total_rent' => $totalRent,
                        'tr_min' => $trMin,
                        'tr_max' => $trMax,
                        'deposit' => $deposit,
                        'units_available' => (int) $this->getVal($fp['UnitsAvailable'] ?? 0),
                        'image_url' => $imageUrl,
                        'image_local_path' => null,
                        'image_local_url' => $localPath,
                        'description' => $this->getVal($fp['Description'] ?? ''),
                        'is_status' => 1
                    ]
                );
                $syncedFloorplanIds[] = $fpModel->id;
            }

            // Clean up floorplans that were not in this sync batch (property-specific)
            if (!empty($syncedFloorplanIds)) {
                FloorPlan::where('property_id', $propId)->whereNotIn('id', $syncedFloorplanIds)->update(['is_status' => 2]);
            }

            // 3. Process Units
            Cache::put("sync_progress_$propId", "Finalizing floorplans...", 300);
            $syncedUnitIds = [];
            $savedUnits = 0;

            Cache::put("sync_progress_$propId", "Step 6: Storing units in database...", 300);
            foreach ($ilsUnits as $unit) {
                // Merge root unit data with nested Units/Unit data to be safe
                $uInfoNested = $unit['Units']['Unit'] ?? [];
                if (isset($uInfoNested[0])) $uInfoNested = $uInfoNested[0];

                $uInfo = array_merge($unit, $uInfoNested);

                $uId = $this->getVal($uInfo['@attributes'] ?? null, 'IDValue') ?: ($uInfo['UnitID'] ?? null);

                if (!$uId) continue;

                $occStatus = strtolower(str_replace('_', ' ', $uInfo['UnitOccupancyStatus'] ?? ''));
                $leasedStatus = strtolower(str_replace('_', ' ', $uInfo['UnitLeasedStatus'] ?? ''));

                $isAvailable = false;
                if (
                    ($occStatus == 'vacant' && ($leasedStatus == 'available' || $leasedStatus == '')) ||
                    ($occStatus == 'occupied' && $leasedStatus == 'on notice')
                ) {
                    $isAvailable = true;
                }

                if (!$isAvailable) continue;

                $unitEffectivePrices = $this->extractLeaseTermPrices($unit, 'EffectiveRent');
                if (!$unitEffectivePrices) $unitEffectivePrices = $this->extractLeaseTermPrices($unit, 'Rent');
                $uRentMin = $unitEffectivePrices ? (float) min($unitEffectivePrices) : (float) $this->getVal($unit['EffectiveRent'] ?? 0, 'Min');
                $unitTotalPrices = $this->extractLeaseTermPrices($unit, 'TotalRent');
                $uTotalMin = $unitTotalPrices ? (float) min($unitTotalPrices) : (float) $this->getVal($unit['TotalRent'] ?? 0, 'Min');
                $unitMarketRent = (float) $this->getVal($unit['MarketRent'] ?? $uInfo['MarketRent'] ?? 0, 'Min');
                // Try to find Deposit in common locations
                $depositData = $unit['Deposit'] ?? $uInfo['Deposit'] ?? $unit['Information']['Deposit'] ?? 0;
                $uDeposit = (float) $this->getVal($depositData, 'Amount');
                if ($uDeposit == 0) $uDeposit = (float) $this->getVal($depositData, 'Value');

                // Fallback: If unit deposit is 0, try to get it from the floorplan (using smart lookup)
                if ($uDeposit == 0) {
                    $fpType = $uInfo['UnitType'] ?? '';
                    $fpName = $uInfo['FloorplanName'] ?? '';

                    $fpForDeposit = FloorPlan::where('property_id', $propId)
                        ->where(function ($q) use ($fpType, $fpName) {
                            if ($fpType) $q->where('floorplan_id', $fpType);
                            if ($fpName) $q->orWhere('name', $fpName);
                        })->first();

                    // Final fallback: Match by beds/baths if name/ID fail
                    if (!$fpForDeposit) {
                        $uBeds = (int) ($uInfo['UnitBedrooms'] ?? 0);
                        $uBaths = (float) ($uInfo['UnitBathrooms'] ?? 0);
                        $fpForDeposit = FloorPlan::where('property_id', $propId)
                            ->where('beds', $uBeds)
                            ->where('baths', $uBaths)
                            ->first();
                    }

                    if ($fpForDeposit) {
                        $uDeposit = (float) $fpForDeposit->deposit;
                    }
                }

                // Try to find Availability in common locations
                $availData = $unit['Availability'] ?? $uInfo['Availability'] ?? $unit['Information']['Availability'] ?? $unit['AvailabilityDetails'] ?? [];

                // Extra deep check for VacateDate and MadeReadyDate
                $vDate = $this->parseMITSDate($availData['VacateDate'] ?? $unit['VacateDate'] ?? $uInfo['VacateDate'] ?? null);
                $mrDate = $this->parseMITSDate($availData['MadeReadyDate'] ?? $availData['AvailableDate'] ?? $availData['AvailabilityDate'] ?? $unit['AvailableDate'] ?? $unit['AvailabilityDate'] ?? $uInfo['AvailableDate'] ?? $uInfo['AvailabilityDate'] ?? null);

                // Final recursive search for any dates if still missing
                if (!$vDate) $vDate = $this->findDeepDate($unit, 'VacateDate');
                if (!$mrDate) $mrDate = $this->findDeepDate($unit, 'ReadyDate') ?: $this->findDeepDate($unit, 'AvailableDate');

                $unitModel = Unit::where('property_id', $propId)->where('unit_id', $uId)->first();

                $uModel = Unit::updateOrCreate(
                    ['property_id' => $propId, 'unit_id' => $uId],
                    [
                        'floorplan_id' => $uInfo['UnitType'] ?? '',
                        'beds' => (int) ($uInfo['UnitBedrooms'] ?? 0),
                        'baths' => (float) ($uInfo['UnitBathrooms'] ?? 0),
                        'sqft' => (int) ($uInfo['MaxSquareFeet'] ?? 0),
                        'market_rent' => $unitMarketRent,
                        'effective_rent' => $uRentMin,
                        'total_rent' => $uTotalMin,
                        'deposit' => $uDeposit,
                        'unit_status' => $uInfo['UnitEconomicStatus'] ?? '',
                        'occupancy_status' => $uInfo['UnitOccupancyStatus'] ?? '',
                        'leased_status' => $uInfo['UnitLeasedStatus'] ?? '',
                        'building_id' => $uInfo['BuildingID'] ?? $uInfo['BuildingName'] ?? '',
                        'unit_name' => $uInfo['FloorplanName'] ?? '',
                        'floor_level' => (int) ($unit['FloorLevel'] ?? 0),
                        'vacate_date' => $vDate ?: ($unitModel ? $unitModel->vacate_date : null),
                        'made_ready_date' => $mrDate ?: ($unitModel ? $unitModel->made_ready_date : null),
                        'is_status' => 1
                    ]
                );
                $syncedUnitIds[] = $uModel->id;
                $savedUnits++;
            }

            // Clean up units that were not in this sync batch (property-specific)
            if (!empty($syncedUnitIds)) {
                Unit::where('property_id', $propId)->whereNotIn('id', $syncedUnitIds)->update(['is_status' => 2]);
            }

            // Per-property Sync Log
            SyncLog::create([
                'status' => 'success',
                'message' => "Property $propId synchronized ($savedUnits units)",
                'property_id' => $propId,
                'api_hits' => ($this->apiHits - $hitsStart),
                'sync_type' => $syncType
            ]);

            Cache::put("sync_progress_$propId", "Sync complete!", 300);

            // Log final counts for this property
            Log::info("Sync completed for property $propId. API Hits this run: " . ($this->apiHits - $hitsStart));

            return [
                'status' => 'success',
                'images_processed' => $imagesProcessed,
                'units_saved' => $savedUnits,
                'api_hits' => ($this->apiHits - $hitsStart)
            ];
        } catch (\Exception $e) {
            Log::error("ResMan Sync Error ($propId): " . $e->getMessage());
            Cache::put("sync_progress_$propId", "Error: " . $e->getMessage(), 300);
            return ['status' => 'error', 'message' => $e->getMessage()];
        }
    }

    /**
     * Extract numeric values for one pricing field from ResMan's lease-term matrix.
     * Handles singleton/list OfferTerm shapes and MITS Value/ValueRange wrappers.
     */
    private function extractLeaseTermPrices($unit, $field)
    {
        $values = [];
        $pricing = $unit['Pricing'] ?? [];
        $terms = $pricing['MITS-OfferTerm'] ?? $pricing['OfferTerm'] ?? $unit['MITS-OfferTerm'] ?? [];
        if ($terms && !isset($terms[0])) $terms = [$terms];

        foreach ((array) $terms as $term) {
            if (!is_array($term) || !array_key_exists($field, $term)) continue;
            $raw = $term[$field];
            $candidates = [];
            if (is_numeric($raw)) {
                $candidates[] = (float) $raw;
            } elseif (is_array($raw)) {
                foreach (['Exact', 'Value', 'Amount', 'Min', 'Max'] as $key) {
                    if (isset($raw['@attributes'][$key]) && is_numeric($raw['@attributes'][$key])) $candidates[] = (float) $raw['@attributes'][$key];
                    if (isset($raw[$key]) && is_numeric($raw[$key])) $candidates[] = (float) $raw[$key];
                }
                if (!$candidates) {
                    $v = $this->getVal($raw);
                    if (is_numeric($v)) $candidates[] = (float) $v;
                }
            } elseif (is_string($raw) && is_numeric($raw)) {
                $candidates[] = (float) $raw;
            }
            foreach ($candidates as $value) {
                if ($value > 0) $values[] = $value;
            }
        }

        // Some responses expose pricing fields directly on the ILS_Unit rather than OfferTerm.
        if (!$values && isset($unit[$field])) {
            $raw = $unit[$field];
            if (is_numeric($raw) && (float) $raw > 0) $values[] = (float) $raw;
            elseif (is_array($raw)) {
                foreach (['Exact', 'Value', 'Amount', 'Min', 'Max'] as $key) {
                    $v = $raw['@attributes'][$key] ?? $raw[$key] ?? null;
                    if (is_numeric($v) && (float) $v > 0) $values[] = (float) $v;
                }
            }
        }

        return array_values(array_unique($values));
    }

    private function getVal($data, $preferredKey = null)
    {
        if ($data === null || $data === '') return '';
        if (!is_array($data)) return $data;

        // Try preferred key first
        if ($preferredKey && isset($data[$preferredKey])) return $this->getVal($data[$preferredKey]);
        if ($preferredKey && isset($data['@attributes']) && isset($data['@attributes'][$preferredKey])) return $data['@attributes'][$preferredKey];

        // Try common MITS keys
        if (isset($data['Amount'])) return $this->getVal($data['Amount']);
        if (isset($data['Value'])) return $this->getVal($data['Value']);
        if (isset($data['ValueRange'])) return $this->getVal($data['ValueRange'], $preferredKey);

        // Try attributes
        if (isset($data['@attributes'])) {
            $attr = $data['@attributes'];
            if (isset($attr['Exact'])) return $attr['Exact'];
            if (isset($attr['Value'])) return $attr['Value'];
            if (isset($attr['Amount'])) return $attr['Amount'];
            if (isset($attr['Max'])) return $attr['Max'];
            if (isset($attr['Min'])) return $attr['Min'];
        }

        if (isset($data[0])) return $this->getVal($data[0], $preferredKey);

        foreach ($data as $v) {
            if (!is_array($v)) return $v;
        }
        return '';
    }

    private function parseMITSDate($date)
    {
        if (empty($date)) return null;

        // Handle Month/Day/Year attributes (as seen in legacy code)
        if (is_array($date) && isset($date['@attributes']['Year'])) {
            $attr = $date['@attributes'];
            $m = str_pad($attr['Month'] ?? '0', 2, '0', STR_PAD_LEFT);
            $d = str_pad($attr['Day'] ?? '0', 2, '0', STR_PAD_LEFT);
            $y = $attr['Year'];
            return "$y-$m-$d";
        }

        // If it's an array, try other common MITS structures
        if (is_array($date)) {
            $date = $date['@attributes']['Exact'] ?? $date['Exact'] ?? $date['Value'] ?? $date[0] ?? null;
        }

        if (empty($date) || is_array($date)) return null;

        $timestamp = strtotime($date);
        if (!$timestamp || $timestamp < strtotime('2000-01-01')) return null;

        try {
            return date('Y-m-d', $timestamp);
        } catch (\Exception $e) {
            return null;
        }
    }

    private function findDeepDate($data, $searchKey)
    {
        if (empty($data)) return null;
        if (!is_array($data)) return null;

        foreach ($data as $key => $val) {
            if (is_string($key) && stripos($key, $searchKey) !== false) {
                $parsed = $this->parseMITSDate($val);
                if ($parsed) return $parsed;
            }
            if (is_array($val)) {
                $res = $this->findDeepDate($val, $searchKey);
                if ($res) return $res;
            }
        }
        return null;
    }

    private function processImage($url, $id, $propertyId)
    {
        if (empty($url)) return null;

        $quality = config('resman.webp_quality', 80);
        $convertToWebp = config('resman.convert_to_webp', true);

        try {
            $response = Http::timeout(120)->connectTimeout(30)->get($url);
            if (!$response->successful()) return null;
            $fileContent = $response->body();

            // Detect extension from URL or fallback to jpg
            $urlPath = parse_url($url, PHP_URL_PATH);
            $origExtension = pathinfo($urlPath, PATHINFO_EXTENSION) ?: 'jpg';

            if (!$convertToWebp) {
                $s3Path = 'floorplans/' . $propertyId . '/' . $id . '.' . $origExtension;
                Storage::disk('s3')->put($s3Path, $fileContent);
                return Storage::disk('s3')->url($s3Path);
            }

            // If we reach here, convertToWebp is true. Use GD.
            $tempFile = tempnam(sys_get_temp_dir(), 'resman_');
            file_put_contents($tempFile, $fileContent);

            $finfo = new finfo(FILEINFO_MIME_TYPE);
            $mimeType = $finfo->file($tempFile);

            $extension = 'webp';
            $s3Path = 'uv-residential/' . $propertyId . '/' . $id . '.' . $extension;

            $img = null;
            switch ($mimeType) {
                case 'image/jpeg':
                    $img = imagecreatefromjpeg($tempFile);
                    break;
                case 'image/png':
                    $img = imagecreatefrompng($tempFile);
                    imagepalettetotruecolor($img);
                    imagealphablending($img, true);
                    imagesavealpha($img, true);
                    break;
                case 'image/gif':
                    $img = imagecreatefromgif($tempFile);
                    break;
            }

            if ($img) {
                $webpTemp = tempnam(sys_get_temp_dir(), 'resman_webp_');
                imagewebp($img, $webpTemp, $quality);
                imagedestroy($img);
                unlink($tempFile);
                Storage::disk('s3')->put($s3Path, file_get_contents($webpTemp));
                unlink($webpTemp);
                return Storage::disk('s3')->url($s3Path);
            }

            unlink($tempFile);
        } catch (\Exception $e) {
            Log::warning("Image processing failed for $url: " . $e->getMessage());
        }

        return null;
    }
}
