<?php

namespace App\Http\Livewire\Calls;

use App\Helpers\ProfileAuthorizationHelper;
use App\Mail\CallBlockedMail;
use App\Models\Call;
use App\Models\CallTranslation;
use App\Models\Locations\Location;
use App\Services\CallCreditService;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use Livewire\Component;
use Livewire\WithPagination;
use WireUi\Traits\WireUiActions;

class Manage extends Component
{
    use WithPagination;
    use WireUiActions;

    public string $search = '';
    public ?string $statusFilter = '';   // 'active', 'expired', 'deleted'
    public ?string $callableFilter = ''; // 'user', 'organization', 'bank'
    public ?string $localeFilter = '';   // 'en', 'nl', 'de', etc.

    public array $bulkSelected = [];
    public bool $selectAll = false;
    public int $perPage = 10;
    public string $sortField = 'id';
    public string $sortDirection = 'desc';

    public bool $isAdminView = false;   // true for Admin/Bank manager, false for own calls

    // Inline edit state
    public ?int $editCallId = null;
    public string $editContent = '';
    public ?string $editTill = null;
    public ?int $editTagId = null;
    public bool $editIsPublic = false;
    public $editCountry = null;
    public $editDivision = null;
    public $editCity = null;
    public $editDistrict = null;
    public bool $showEditModal = false;
    public bool $showDeleteConfirm = false;

    // Admin pause/publish confirmation
    public bool $showAdminActionConfirm = false;
    public ?int $adminActionCallId = null;
    public string $adminActionType = ''; // 'pause' or 'publish'
    public string $adminActionCallableName = '';

    // Admin bulk delete confirmation
    public bool $showAdminDeleteConfirm = false;
    public string $adminDeleteCallableNames = '';

    // Non-admin bulk delete confirmation
    public bool $showDeleteConfirmModal = false;

    protected $listeners = [
        'countryToParent',
        'divisionToParent',
        'cityToParent',
        'districtToParent',
        'callTagSelected',
        'callTagCleared',
        'callSaved' => '$refresh',
    ];

    protected $queryString = [
        'search'         => ['except' => ''],
        'statusFilter'   => ['except' => ''],
        'callableFilter' => ['except' => ''],
        'localeFilter'   => ['except' => ''],
        'perPage'        => ['except' => 10],
        'sortField'      => ['except' => 'id'],
        'sortDirection'  => ['except' => 'desc'],
    ];

    public function mount(): void
    {
        $activeProfileType = session('activeProfileType');
        $activeProfileId   = session('activeProfileId');

        if (!$activeProfileType || !$activeProfileId) {
            abort(403);
        }

        $profile = $activeProfileType::find($activeProfileId);
        if (!$profile) {
            abort(403);
        }

        ProfileAuthorizationHelper::authorize($profile);

        // Admin and Central Bank get full view of all calls
        if ($profile instanceof \App\Models\Admin) {
            $this->isAdminView = true;
        } elseif ($profile instanceof \App\Models\Bank && $profile->level === 0) {
            $this->isAdminView = true;
        } elseif ($profile instanceof \App\Models\User
            || $profile instanceof \App\Models\Organization
            || $profile instanceof \App\Models\Bank) {
            $this->isAdminView = false;
        } else {
            abort(403);
        }
    }

    // Listeners for location dropdown child component
    public function countryToParent($value): void  { $this->editCountry  = $value ?: null; }
    public function divisionToParent($value): void { $this->editDivision = $value ?: null; }
    public function cityToParent($value): void     { $this->editCity     = $value ?: null; }
    public function districtToParent($value): void { $this->editDistrict = $value ?: null; }

    public function callTagSelected(int $tagId): void
    {
        $this->editTagId = $tagId;
        $this->resetValidation('editTagId');
    }

    public function callTagCleared(): void
    {
        $this->editTagId = null;
    }

    public function updatedShowEditModal(bool $value): void
    {
        if (!$value) {
            $this->dispatch('edit-done');
        }
    }

    public function openEdit(int $id): void
    {
        $call = $this->findCall($id);

        $this->editCallId  = $call->id;
        $this->editTagId   = $call->tag_id;
        $this->editContent = $call->translations->firstWhere('locale', App::getLocale())?->content
            ?? $call->translations->first()?->content
            ?? '';
        $this->editTill     = $call->till?->format('Y-m-d');
        $this->editIsPublic = (bool) $call->is_public;

        $location = $call->location;
        $this->editCountry  = $location?->country_id;
        $this->editDivision = $location?->division_id;
        $this->editCity     = $location?->city_id;
        $this->editDistrict = $location?->district_id;

        $this->resetValidation();
        $this->showEditModal = true;
    }

    protected function editRules(): array
    {
        $call = $this->editCallId ? Call::find($this->editCallId) : null;
        $callableType = $call?->callable_type ?? session('activeProfileType');
        $tillMaxDays = ($callableType && $callableType !== \App\Models\User::class)
            ? timebank_config('calls.till_max_days_non_user')
            : timebank_config('calls.till_max_days');

        $tillRule = 'required|date|after:today';
        if ($tillMaxDays !== null) {
            $tillRule .= '|before_or_equal:' . now()->addDays($tillMaxDays)->format('Y-m-d');
        }

        return [
            'editContent' => ['required', 'string', 'max:' . timebank_config('calls.content_max_input', 200)],
            'editTill'    => $tillRule,
            'editTagId'   => 'required|integer|exists:taggable_tags,tag_id',
            'editCountry' => 'required|integer|exists:countries,id',
            'editCity'    => 'nullable|integer|exists:cities,id',
            'editDistrict'=> 'nullable|integer|exists:districts,id',
        ];
    }

    public function saveEdit(): void
    {
        $this->validate($this->editRules());

        $call = $this->findCall($this->editCallId);

        $locationId = null;
        if ($this->editCountry || $this->editCity) {
            $attributes = array_filter([
                'country_id'  => $this->editCountry  ?: null,
                'division_id' => $this->editDivision ?: null,
                'city_id'     => $this->editCity     ?: null,
                'district_id' => $this->editDistrict ?: null,
            ]);
            $location = Location::whereNull('locatable_id')
                ->whereNull('locatable_type')
                ->where($attributes)
                ->first();
            if (!$location) {
                $location = new Location($attributes);
                $location->save();
            }
            $locationId = $location->id;
        }

        $call->update([
            'tag_id'      => $this->editTagId,
            'location_id' => $locationId,
            'till'        => $this->editTill ?: null,
            'is_public'   => $this->editIsPublic,
        ]);

        CallTranslation::updateOrCreate(
            ['call_id' => $call->id, 'locale' => App::getLocale()],
            ['content' => $this->editContent ?: null]
        );

        $call->searchable();

        $this->showEditModal = false;
        $this->editCallId    = null;
        $this->dispatch('edit-done');
        $this->notification()->success(title: __('Saved'), description: __('Your Call has been updated.'));
    }

    public function confirmDelete(): void
    {
        $this->showDeleteConfirm = true;
    }

    public function deleteCall(): void
    {
        $call = $this->findCall($this->editCallId);
        $call->unsearchable();
        $call->translations()->delete();
        $call->delete();

        $this->showEditModal    = false;
        $this->showDeleteConfirm = false;
        $this->editCallId       = null;
        $this->notification()->success(title: __('Deleted'), description: __('The call has been deleted.'));
    }

    public function sortBy(string $field): void
    {
        if ($this->sortField === $field) {
            $this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortField    = $field;
            $this->sortDirection = 'desc';
        }
        $this->resetPage();
    }

    public function updatedSearch(): void    { $this->resetPage(); }
    public function updatedStatusFilter(): void  { $this->resetPage(); $this->bulkSelected = []; }
    public function updatedCallableFilter(): void { $this->resetPage(); $this->bulkSelected = []; }
    public function updatedLocaleFilter(): void  { $this->resetPage(); $this->bulkSelected = []; }
    public function updatedPerPage(): void   { $this->resetPage(); }

    public function updatedSelectAll(bool $value): void
    {
        if ($value) {
            $activeProfileType = session('activeProfileType');
            $activeProfileId   = session('activeProfileId');
            $query = Call::query();
            if (!$this->isAdminView) {
                $query->where('callable_type', $activeProfileType)->where('callable_id', $activeProfileId);
            }
            if ($this->statusFilter === 'deleted') {
                $query->onlyTrashed();
            }
            $this->bulkSelected = $query->pluck('id')->map(fn ($id) => (string) $id)->toArray();
        } else {
            $this->bulkSelected = [];
        }
    }

    public function confirmBulkDelete(): void
    {
        $this->showDeleteConfirmModal = true;
    }

    public function confirmAdminDelete(): void
    {
        $calls = Call::whereIn('id', $this->bulkSelected)
            ->with('callable')
            ->get();

        $nameList = $calls->map(fn ($c) => $c->callable?->name ?? '?')->unique()->values()->all();
        $names = count($nameList) > 1
            ? implode(', ', array_slice($nameList, 0, -1)) . ' ' . __('and') . ' ' . end($nameList)
            : ($nameList[0] ?? '?');
        $this->adminDeleteCallableNames = $names;
        $this->showAdminDeleteConfirm   = true;
    }

    public function deleteSelected(): void
    {
        $this->authorizeWrite();

        $calls = Call::whereIn('id', $this->bulkSelected)->get();
        foreach ($calls as $call) {
            $call->unsearchable();
            $call->translations()->delete();
            $call->delete();
        }

        $this->bulkSelected             = [];
        $this->selectAll                = false;
        $this->showAdminDeleteConfirm   = false;
        $this->adminDeleteCallableNames = '';
        $this->showDeleteConfirmModal   = false;
        $this->notification()->success(title: __('Deleted'), description: __('Selected calls have been deleted.'));
    }

    public function undeleteSelected(): void
    {
        $this->authorizeWrite();

        $calls = Call::onlyTrashed()->whereIn('id', $this->bulkSelected)->get();
        foreach ($calls as $call) {
            $call->translations()->withTrashed()->restore();
            $call->restore();
            $call->searchable();
        }

        $this->bulkSelected = [];
        $this->selectAll    = false;
        $this->notification()->success(title: __('Restored'), description: __('Selected calls have been restored.'));
    }

    public function confirmAdminAction(int $id, string $type): void
    {
        $call = $this->findCall($id);
        $this->adminActionCallId      = $id;
        $this->adminActionType        = $type;
        $this->adminActionCallableName = $call->callable?->name ?? '?';
        $this->showAdminActionConfirm = true;
        $this->dispatch('admin-action-ready');
    }

    public function executeAdminAction(): void
    {
        if (!$this->adminActionCallId || !$this->adminActionType) {
            return;
        }
        if ($this->adminActionType === 'pause') {
            $this->adminPause($this->adminActionCallId);
        } elseif ($this->adminActionType === 'publish') {
            $this->adminPublish($this->adminActionCallId);
        }
        $this->showAdminActionConfirm = false;
        $this->adminActionCallId      = null;
        $this->adminActionType        = '';
        $this->adminActionCallableName = '';
    }

    public function adminPause(int $id): void
    {
        if (!$this->isAdminView) abort(403);
        $call = Call::findOrFail($id);
        $call->update(['is_paused' => true]);
        $call->unsearchable();
        $this->dispatch('pause-publish-done');
        $this->notification()->success(title: __('Paused'), description: __('The call has been paused.'));
    }

    public function adminPublish(int $id): void
    {
        if (!$this->isAdminView) abort(403);
        $call = Call::findOrFail($id);
        $call->update(['is_paused' => false]);
        if ($call->shouldBeSearchable()) {
            $call->searchable();
        }
        $this->dispatch('pause-publish-done');
        $this->notification()->success(title: __('Published'), description: __('The call has been published.'));
    }

    public function pause(int $id): void
    {
        $this->authorizeWrite();
        $call = $this->findCall($id);
        $call->update(['is_paused' => true]);
        $call->unsearchable();
        $this->dispatch('pause-publish-done');
        $this->notification()->success(title: __('Paused'), description: __('The call has been paused.'));
    }

    public function publish(int $id): void
    {
        $this->authorizeWrite();
        $call = $this->findCall($id);

        if (!CallCreditService::profileHasCredits($call->callable_type, $call->callable_id)) {
            $this->notification()->error(
                title: __('Cannot publish'),
                description: trans_with_platform(__('You need @PLATFORM_CURRENCY_NAME_PLURAL@ to post a call.'))
            );
            $this->dispatch('pause-publish-done');
            return;
        }

        // If till has expired, reset it to the max allowed duration
        $updates = ['is_paused' => false];
        if ($call->till === null || $call->till->isPast()) {
            $updates['till'] = now()->addDays(timebank_config('calls.till_max_days', 90));
        }
        $call->update($updates);
        $call->searchable();
        $this->dispatch('pause-publish-done');
        $this->notification()->success(title: __('Published'), description: __('The call has been published.'));
    }

    public function block(int $id): void
    {
        if (!$this->isAdminView) {
            abort(403);
        }
        $call = Call::findOrFail($id);
        $call->update(['is_suppressed' => true]);
        $call->unsearchable();

        $callable = $call->callable;
        if ($callable && $callable->email) {
            Mail::to($callable->email)->queue(
                new CallBlockedMail(
                    $call->load(['tag']),
                    $callable,
                    class_basename($callable)
                )
            );
        }

        $this->notification()->error(title: __('Blocked'), description: __('The call has been blocked.'));
    }

    public function unblock(int $id): void
    {
        if (!$this->isAdminView) {
            abort(403);
        }
        $call = Call::findOrFail($id);
        $call->update(['is_suppressed' => false]);
        if ($call->shouldBeSearchable()) {
            $call->searchable();
        }
        $this->notification()->success(title: __('Unblocked'), description: __('The call has been unblocked.'));
    }

    private function findCall(int $id): Call
    {
        if ($this->isAdminView) {
            return Call::findOrFail($id);
        }
        return Call::where('callable_type', session('activeProfileType'))
                   ->where('callable_id', session('activeProfileId'))
                   ->findOrFail($id);
    }

    private function authorizeWrite(): void
    {
        $activeProfileType = session('activeProfileType');
        $activeProfileId   = session('activeProfileId');
        if (!$activeProfileType || !$activeProfileId) {
            abort(403);
        }
        $profile = $activeProfileType::find($activeProfileId);
        ProfileAuthorizationHelper::authorize($profile);
    }

    public function render()
    {
        $activeProfileType = session('activeProfileType');
        $activeProfileId   = session('activeProfileId');

        $query = Call::with([
            'callable',
            'tag.contexts.category',
            'location.city.translations',
            'location.country.translations',
            'translations',
        ]);

        // Scope to own calls for non-admin
        if (!$this->isAdminView) {
            $query->where('callable_type', $activeProfileType)
                  ->where('callable_id', $activeProfileId);
        }

        // Callable type filter (admin view)
        if ($this->isAdminView && $this->callableFilter) {
            $map = [
                'user'         => \App\Models\User::class,
                'organization' => \App\Models\Organization::class,
                'bank'         => \App\Models\Bank::class,
            ];
            if (isset($map[$this->callableFilter])) {
                $query->where('callable_type', $map[$this->callableFilter]);
            }
        }

        // Status / deleted
        if ($this->statusFilter === 'deleted') {
            $query->onlyTrashed();
        } elseif ($this->statusFilter === 'active') {
            $query->where('is_paused', false)
                  ->where(fn ($q) => $q->whereNull('till')->orWhere('till', '>=', now()));
        } elseif ($this->statusFilter === 'expired') {
            $query->where('till', '<', now());
        } elseif ($this->statusFilter === 'paused') {
            $query->where('is_paused', true);
        }

        // Search
        if ($this->search) {
            $search = '%' . $this->search . '%';
            $query->where(function ($q) use ($search) {
                $q->whereHas('translations', fn ($t) => $t->where('content', 'like', $search))
                  ->orWhereHas('tag', fn ($t) => $t->where('name', 'like', $search))
                  ->orWhereHas('callable', fn ($t) => $t->where('name', 'like', $search));
            });
        }

        // Locale filter
        if ($this->localeFilter) {
            $query->whereHas('translations', fn ($q) => $q->where('locale', $this->localeFilter));
        }

        $allowedSorts = ['id', 'created_at', 'till', 'updated_at'];
        if (in_array($this->sortField, $allowedSorts)) {
            $query->orderBy($this->sortField, $this->sortDirection);
        } else {
            // Sort by first locale via subquery to avoid duplicate rows
            $localeSubquery = DB::table('call_translations')
                ->select('call_id', DB::raw('MIN(locale) as first_locale'))
                ->whereNull('deleted_at')
                ->groupBy('call_id');

            $query->leftJoinSub($localeSubquery, 'ct_locale', 'ct_locale.call_id', '=', 'calls.id')
                ->select('calls.*')
                ->orderBy('ct_locale.first_locale', $this->sortDirection)
                ->orderBy('calls.created_at', 'desc');
        }

        $calls = $query->paginate($this->perPage);

        $editCall = $this->editCallId ? Call::find($this->editCallId) : null;

        // For non-admin view, check if the active profile has credits
        $canPublish = true;
        if (!$this->isAdminView && $activeProfileType && $activeProfileId) {
            $canPublish = CallCreditService::profileHasCredits($activeProfileType, (int) $activeProfileId);
        }

        $availableLocales = \App\Models\CallTranslation::select('locale')
            ->distinct()
            ->orderBy('locale')
            ->pluck('locale')
            ->map(fn ($locale) => ['id' => $locale, 'name' => strtoupper($locale)])
            ->values()
            ->all();

        return view('livewire.calls.manage', [
            'calls'            => $calls,
            'bulkDisabled'     => empty($this->bulkSelected),
            'editCall'         => $editCall,
            'canPublish'       => $canPublish,
            'availableLocales' => $availableLocales,
        ]);
    }
}
