<?php

namespace App\Models;

use App\CentralLogics\Helpers;
use App\Scopes\StoreScope;
use App\Scopes\ZoneScope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class Review extends Model
{
    use HasFactory;

    protected ?OrderDetail $resolvedOrderDetailCache = null;
    protected bool $resolvedOrderDetailLoaded = false;
    protected static ?array $columnAvailabilityCache = null;
    protected $appends = [
        'display_item_name',
        'display_item_image_full_url',
        'display_item_id',
        'display_store_name',
        'display_customer_name',
        'display_customer_image_full_url',
        'attachment_full_urls',
    ];

    protected $casts = [
        'item_id' => 'integer',
        'item_campaign_id' => 'integer',
        'user_id' => 'integer',
        'order_id' => 'integer',
        'rating' => 'integer',
        'store_id' => 'integer',
        'reward_points' => 'integer',
        'reward_points_granted_at' => 'datetime',
        'reward_popup_seen_at' => 'datetime',
        'created_at' => 'datetime',
        'updated_at' => 'datetime'
    ];

    public function scopeModule($query, $module_id)
    {
        return $query->where(function ($query) use ($module_id) {
            $hasCondition = false;

            if (static::hasColumn('module_id')) {
                $query->where('module_id', $module_id);
                $hasCondition = true;
            }

            if (static::hasColumn('item_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('item', function ($itemQuery) use ($module_id) {
                    $itemQuery->withoutGlobalScopes()->where('module_id', $module_id);
                });
                $hasCondition = true;
            }

            if (static::hasColumn('item_campaign_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('itemCampaign', function ($campaignQuery) use ($module_id) {
                    $campaignQuery->withoutGlobalScopes()->where('module_id', $module_id);
                });
                $hasCondition = true;
            }

            if (static::hasColumn('order_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('order.store', function ($storeQuery) use ($module_id) {
                    $storeQuery->where('module_id', $module_id);
                });
                $hasCondition = true;
            }

            if (! $hasCondition) {
                $query->whereRaw('1 = 0');
            }
        });
    }

    public function item()
    {
        return $this->belongsTo(Item::class)
            ->withoutGlobalScopes([StoreScope::class, ZoneScope::class]);
    }

    public function itemCampaign()
    {
        return $this->belongsTo(ItemCampaign::class, 'item_campaign_id')
            ->withoutGlobalScopes([StoreScope::class, ZoneScope::class]);
    }

    public function scopeForStore($query, int $storeId)
    {
        return $query->where(function ($query) use ($storeId) {
            $hasCondition = false;

            if (static::hasColumn('store_id')) {
                $query->where('store_id', $storeId);
                $hasCondition = true;
            }

            if (static::hasColumn('item_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('item', function ($itemQuery) use ($storeId) {
                    $itemQuery->withoutGlobalScopes()->where('store_id', $storeId);
                });
                $hasCondition = true;
            }

            if (static::hasColumn('item_campaign_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('itemCampaign', function ($campaignQuery) use ($storeId) {
                    $campaignQuery->withoutGlobalScopes()->where('store_id', $storeId);
                });
                $hasCondition = true;
            }

            if (static::hasColumn('order_id')) {
                $method = $hasCondition ? 'orWhereHas' : 'whereHas';
                $query->{$method}('order', function ($orderQuery) use ($storeId) {
                    $orderQuery->where('store_id', $storeId);
                });
                $hasCondition = true;
            }

            if (! $hasCondition) {
                $query->whereRaw('1 = 0');
            }
        });
    }

    public function customer()
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    public function store()
    {
        return $this->belongsTo(Store::class, 'store_id');
    }

    public function order()
    {
        return $this->belongsTo(Order::class, 'order_id');
    }

    public function scopeActive($query)
    {
        return static::hasColumn('status') ? $query->where('status', 1) : $query;
    }

    protected static function boot()
    {
        parent::boot();
        static::saved(function ($review) {
            if(static::hasColumn('review_id') && $review->review_id == null){
                $review->review_id = $review->generateReviewId($review->order_id);
                $review->save();
            }
        });
    }

    public static function hasColumn(string $column): bool
    {
        if (self::$columnAvailabilityCache === null) {
            self::$columnAvailabilityCache = [];
        }

        if (! array_key_exists($column, self::$columnAvailabilityCache)) {
            self::$columnAvailabilityCache[$column] = Schema::hasColumn((new static())->getTable(), $column);
        }

        return (bool) self::$columnAvailabilityCache[$column];
    }
    private function generateReviewId($id)
    {
        $review_id = Str::slug($id);
        if ($max_review_id = static::where('review_id', 'like',"{$review_id}%")->latest('id')->value('review_id')) {

            if($max_review_id == $review_id) return "{$review_id}-2";

            $max_review_id = explode('-',$max_review_id);
            $count = array_pop($max_review_id);
            if (isset($count) && is_numeric($count)) {
                $max_review_id[]= ++$count;
                return implode('-', $max_review_id);
            }
        }
        return $review_id;
    }

    public function getDisplayItemNameAttribute(): string
    {
        if (!empty($this->item?->name)) {
            return $this->item->name;
        }

        $campaignName = trim((string) ($this->itemCampaign?->title ?? $this->itemCampaign?->name ?? ''));
        if ($campaignName !== '') {
            return $campaignName;
        }

        $orderDetail = $this->resolveOrderDetail();
        $orderDetailDisplayName = trim((string) ($orderDetail?->display_item_name ?? ''));
        if ($this->hasMeaningfulItemName($orderDetailDisplayName)) {
            return $orderDetailDisplayName;
        }

        if (!empty($orderDetail?->item?->name)) {
            return $orderDetail->item->name;
        }

        $detailCampaignName = trim((string) ($orderDetail?->campaign?->title ?? $orderDetail?->campaign?->name ?? ''));
        if ($detailCampaignName !== '') {
            return $detailCampaignName;
        }

        $snapshot = $this->getOrderDetailSnapshotItemData();
        $name = trim((string) (
            data_get($snapshot, 'name')
            ?? data_get($snapshot, 'title')
            ?? data_get($snapshot, 'item_name')
            ?? data_get($snapshot, 'product_name')
            ?? data_get($snapshot, 'campaign_title')
            ?? data_get($snapshot, 'item.name')
            ?? data_get($snapshot, 'item.title')
            ?? data_get($snapshot, 'campaign.title')
            ?? data_get($snapshot, 'campaign.name')
            ?? ''
        ));

        return $this->hasMeaningfulItemName($name) ? $name : translate('messages.Item deleted!');
    }

    public function getDisplayItemImageFullUrlAttribute(): ?string
    {
        if (!empty($this->item?->image_full_url)) {
            return $this->item->image_full_url;
        }

        if (!empty($this->itemCampaign?->image_full_url)) {
            return $this->itemCampaign->image_full_url;
        }

        $orderDetail = $this->resolveOrderDetail();
        if (!empty($orderDetail?->display_item_image_full_url)) {
            return $orderDetail->display_item_image_full_url;
        }

        if (!empty($orderDetail?->item?->image_full_url)) {
            return $orderDetail->item->image_full_url;
        }

        if (!empty($orderDetail?->campaign?->image_full_url)) {
            return $orderDetail->campaign->image_full_url;
        }

        $snapshot = $this->getOrderDetailSnapshotItemData();
        $fullUrl = trim((string) (
            data_get($snapshot, 'image_full_url')
            ?? data_get($snapshot, 'item.image_full_url')
            ?? data_get($snapshot, 'campaign.image_full_url')
            ?? ''
        ));
        if ($fullUrl !== '') {
            return $fullUrl;
        }

        $image = data_get($snapshot, 'image')
            ?? data_get($snapshot, 'img')
            ?? data_get($snapshot, 'item.image')
            ?? data_get($snapshot, 'campaign.image')
            ?? data_get($snapshot, 'images.0.img')
            ?? data_get($snapshot, 'images.0');
        if (!empty($image)) {
            return Helpers::get_full_url('product', $image, data_get($snapshot, 'storage', 'public'));
        }

        return null;
    }

    public function getDisplayItemIdAttribute(): ?int
    {
        $candidateId = (int) ($this->item?->id ?? $this->item_id ?? 0);
        if ($candidateId > 0) {
            return $candidateId;
        }

        $campaignId = (int) ($this->itemCampaign?->id ?? $this->item_campaign_id ?? 0);
        if ($campaignId > 0) {
            return $campaignId;
        }

        $orderDetail = $this->resolveOrderDetail();
        $detailReferenceId = (int) ($orderDetail?->display_item_reference_id ?? 0);
        if ($detailReferenceId > 0) {
            return $detailReferenceId;
        }

        $detailItemId = (int) ($orderDetail?->item?->id ?? $orderDetail?->item_id ?? 0);
        if ($detailItemId > 0) {
            return $detailItemId;
        }

        $detailCampaignId = (int) ($orderDetail?->campaign?->id ?? $orderDetail?->item_campaign_id ?? 0);
        if ($detailCampaignId > 0) {
            return $detailCampaignId;
        }

        $snapshot = $this->getOrderDetailSnapshotItemData();
        foreach (['item_id', 'id', 'product_id'] as $candidateKey) {
            $candidate = (int) data_get($snapshot, $candidateKey, 0);
            if ($candidate > 0) {
                return $candidate;
            }
        }

        return null;
    }

    public function getDisplayItemAvgRatingAttribute(): float
    {
        if (!empty($this->item?->avg_rating)) {
            return (float) $this->item->avg_rating;
        }

        if (!empty($this->resolveOrderDetail()?->item?->avg_rating)) {
            return (float) $this->resolveOrderDetail()->item->avg_rating;
        }

        return 0.0;
    }

    public function getDisplayStoreNameAttribute(): string
    {
        if (!empty($this->item?->store?->name)) {
            return $this->item->store->name;
        }

        if (!empty($this->itemCampaign?->store?->name)) {
            return $this->itemCampaign->store->name;
        }

        if (!empty($this->store?->name)) {
            return $this->store->name;
        }

        if (!empty($this->resolveOrderDetail()?->item?->store?->name)) {
            return $this->resolveOrderDetail()->item->store->name;
        }

        if (!empty($this->resolveOrderDetail()?->campaign?->store?->name)) {
            return $this->resolveOrderDetail()->campaign->store->name;
        }

        return translate('messages.store_deleted');
    }

    public function getDisplayCustomerNameAttribute(): string
    {
        $fullName = trim((string) ($this->customer?->full_name ?? ''));
        if ($fullName !== '') {
            return $fullName;
        }

        $firstName = trim((string) ($this->customer?->f_name ?? ''));
        $lastName = trim((string) ($this->customer?->l_name ?? ''));
        $combined = trim($firstName.' '.$lastName);

        return $combined !== '' ? $combined : translate('messages.customer_not_found');
    }

    public function getDisplayCustomerImageFullUrlAttribute(): ?string
    {
        return $this->customer?->image_full_url;
    }

    public function getAttachmentFullUrlsAttribute(): array
    {
        $attachment = $this->attachment;

        if (is_string($attachment)) {
            $decoded = json_decode($attachment, true);
            $attachment = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
        }

        if (! is_array($attachment)) {
            return [];
        }

        $fullUrls = [];
        foreach ($attachment as $item) {
            if (! $item) {
                continue;
            }

            if (is_string($item)) {
                $fullUrls[] = Helpers::get_full_url('review', $item, 'public');
                continue;
            }

            if (is_array($item)) {
                $image = data_get($item, 'img', data_get($item, 'image', data_get($item, 'path')));
                if ($image) {
                    $fullUrls[] = Helpers::get_full_url('review', $image, data_get($item, 'storage', 'public'));
                }
            }
        }

        return $fullUrls;
    }

    private function getOrderDetailSnapshotItemData(): array
    {
        $orderDetail = $this->resolveOrderDetail();
        if (!$orderDetail || empty($orderDetail->item_details)) {
            return [];
        }

        $displayDetails = $orderDetail->display_item_details;
        if (is_array($displayDetails) && ! empty($displayDetails)) {
            $displayName = trim((string) (
                data_get($displayDetails, 'name')
                ?? data_get($displayDetails, 'title')
                ?? data_get($displayDetails, 'item_name')
                ?? data_get($displayDetails, 'product_name')
                ?? data_get($displayDetails, 'campaign_title')
                ?? ''
            ));

            if ($this->hasMeaningfulItemName($displayName) || ! empty(data_get($displayDetails, 'image_full_url')) || (int) data_get($displayDetails, 'item_id', data_get($displayDetails, 'item_campaign_id', 0)) > 0) {
                return $displayDetails;
            }
        }

        $formattedDetails = Helpers::order_details_data_formatting([$orderDetail]);
        $formattedSnapshot = data_get($formattedDetails, '0.item_details', []);
        if (is_array($formattedSnapshot) && ! empty($formattedSnapshot)) {
            $formattedName = trim((string) (
                data_get($formattedSnapshot, 'name')
                ?? data_get($formattedSnapshot, 'title')
                ?? data_get($formattedSnapshot, 'item_name')
                ?? data_get($formattedSnapshot, 'product_name')
                ?? data_get($formattedSnapshot, 'campaign_title')
                ?? ''
            ));

            if ($this->hasMeaningfulItemName($formattedName)) {
                return $formattedSnapshot;
            }

            if (! empty(data_get($formattedSnapshot, 'image_full_url')) || (int) data_get($formattedSnapshot, 'item_id', data_get($formattedSnapshot, 'item_campaign_id', 0)) > 0) {
                return $formattedSnapshot;
            }
        }

        $itemDetails = $orderDetail->item_details;
        if (is_string($itemDetails)) {
            $decoded = json_decode($itemDetails, true);
            $itemDetails = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
        }

        if (!is_array($itemDetails)) {
            return [];
        }

        if (
            empty(data_get($itemDetails, 'name'))
            || in_array(strtolower(trim((string) data_get($itemDetails, 'name'))), ['item', 'campaign item'])
        ) {
            $title = trim((string) data_get($itemDetails, 'title', ''));
            if ($title !== '') {
                $itemDetails['name'] = $title;
            }
        }

        return $itemDetails;
    }

    private function resolveOrderDetail(): ?OrderDetail
    {
        if ($this->resolvedOrderDetailLoaded) {
            return $this->resolvedOrderDetailCache;
        }

        $details = collect();
        if ($this->relationLoaded('order') && $this->order && $this->order->relationLoaded('details')) {
            $details = $this->order->details;
        } elseif ($this->order_id) {
            $details = OrderDetail::withoutGlobalScopes()
                ->with([
                    'item' => fn ($query) => $query->withoutGlobalScopes(),
                    'campaign' => fn ($query) => $query->withoutGlobalScopes(),
                ])
                ->where('order_id', $this->order_id)
                ->get();
        }

        $this->resolvedOrderDetailCache = $this->hydrateResolvedOrderDetailRelations(
            $this->matchOrderDetail($details)
        );
        $this->resolvedOrderDetailLoaded = true;

        return $this->resolvedOrderDetailCache;
    }

    private function matchOrderDetail(Collection $details): ?OrderDetail
    {
        $itemId = (int) ($this->item_id ?? 0);
        $campaignId = (int) ($this->item_campaign_id ?? 0);

        if ($itemId > 0) {
            $matched = $details->first(function (OrderDetail $detail) use ($itemId) {
                return (int) ($detail->item_id ?? 0) === $itemId
                    || (int) ($detail->product_id ?? 0) === $itemId
                    || (int) ($detail->display_item_reference_id ?? 0) === $itemId;
            });

            if ($matched) {
                return $matched;
            }
        }

        if ($campaignId > 0) {
            $matchedCampaign = $details->first(function (OrderDetail $detail) use ($campaignId) {
                return (int) $detail->item_campaign_id === $campaignId;
            });

            if ($matchedCampaign) {
                return $matchedCampaign;
            }
        }

        return $details->first();
    }

    private function hydrateResolvedOrderDetailRelations(?OrderDetail $detail): ?OrderDetail
    {
        if (! $detail) {
            return null;
        }

        $itemReferenceId = (int) (
            $detail->item_id
            ?? $detail->product_id
            ?? $detail->display_item_reference_id
            ?? 0
        );

        if (! $detail->relationLoaded('item') && $itemReferenceId > 0) {
            $detail->setRelation('item', Item::withoutGlobalScopes()->find($itemReferenceId));
        }

        if (! $detail->relationLoaded('campaign') && (int) ($detail->item_campaign_id ?? 0) > 0) {
            $detail->setRelation('campaign', ItemCampaign::withoutGlobalScopes()->find($detail->item_campaign_id));
        }

        return $detail;
    }

    private function hasMeaningfulItemName(?string $value): bool
    {
        $name = strtolower(trim((string) ($value ?? '')));

        return $name !== '' && ! in_array($name, ['item', 'campaign item', 'item deleted!'], true);
    }
}
