<?php

namespace App\Filament\Pages;

use App\Enums\DataChangeStatus;
use App\Enums\MediaClassification;
use App\Enums\MediaType;
use App\Enums\RoleEnum;
use App\Enums\VerificationStatus;
use App\Filament\Resources\Manage\DataChanges\DataChangesResource;
use App\Filament\Support\CheerfulNotification;
use App\Models\Company;
use App\Models\DataChangeRequest;
use App\Models\PartnerMedia;
use App\Models\User;
use App\Models\VerificationRequest;
use App\Notifications\BroadcastNotification;
use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload;
use BackedEnum;
use BezhanSalleh\FilamentShield\Traits\HasPageShield;
use Filament\Actions\Action;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Schemas\Components\Group;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Support\Contracts\HasLabel;
use Filament\Support\Enums\Width;
use Filament\Support\Icons\Heroicon;
use Illuminate\Support\Facades\Storage;
use UnitEnum;

class Media extends Page implements HasForms
{
    use HasPageShield;

    public ?Company $company = null;

    public array $data = [];

    public array $originalDocuments = [];

    public ?PartnerMedia $media = null;

    protected static ?string $model = PartnerMedia::class;

    protected static string|UnitEnum|null $navigationGroup = 'Kelola';

    protected static string|BackedEnum|null $navigationIcon = Heroicon::Newspaper;

    protected static ?string $navigationLabel = 'Media';

    protected static ?int $navigationSort = 3;

    protected static ?string $recordTitleAttribute = 'name';

    public bool $restricted = false;

    public bool $isVerificationStage = false;

    protected static ?string $slug = 'manage/media';

    protected static ?string $title = 'Media';

    public ?VerificationRequest $verificationRequest = null;

    protected string $view = 'filament.pages.media';

    public function mount(): void
    {
        if (auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) && ! auth()->user()->company) {
            $this->restricted = true;

            return;
        }

        $company = auth()->user()->company;

        if (! $company) {
            $this->restricted = true;

            return;
        }

        $this->company = $company;

        $this->isVerificationStage = in_array($company?->verificationRequest?->status, [
            VerificationStatus::PENDING,
            VerificationStatus::REJECTED,
        ]) || $company->verificationRequest()->pending()->exists();

        $verificationRequest = $this->company->verificationRequest()
            ->latest()
            ->first();

        $this->verificationRequest = $verificationRequest;

        $media = $company->partnerMedia;

        if (! $media) {
            return;
        }

        $this->media = $media;

        $this->form->fill($media->toArray());

        $documents = [
            'journalism_organization',
            'press_council_certificate',
        ];

        $mediaItems = $media->getMedia('partnerMedia');

        foreach ($documents as $docType) {
            $mediaItem = $mediaItems
                ->where('custom_properties.doc_type', str($docType)->replace('_docs', '')->slug('-'))
                ->sortByDesc('created_at')
                ->first();

            if ($mediaItem) {
                $path = $mediaItem->getPathRelativeToRoot();
                $this->data["{$docType}_docs"] = [$path];
                $this->originalDocuments["{$docType}_docs"] = [$path];
            }
        }
    }

    public static function form(Schema $schema): Schema
    {
        $documents = [
            [
                'title' => 'Organisasi Kewartawanan',
                'text_name' => 'journalism_organization',
                'placeholder' => '*****',
                'max' => 255,
                'max_size' => 1024 * 10,
                'file_name' => 'journalism_organization_docs',
                'accept' => ['application/pdf'],
                'folder' => 'media/journalism-organization/',
            ],
            [
                'title' => 'Sertifikat Dewan Pers',
                'text_name' => 'press_council_certificate',
                'placeholder' => '*****',
                'max' => 255,
                'max_size' => 1024 * 10,
                'file_name' => 'press_council_certificate_docs',
                'accept' => ['application/pdf'],
                'folder' => 'media/press-council-certificate/',
            ],
        ];

        return $schema
            ->components([
                Section::make('Informasi')
                    ->schema([
                        TextInput::make('name')
                            ->label('Nama')
                            ->placeholder('PWK News')
                            ->autocomplete(false)
                            ->autofocus()
                            ->required()
                            ->maxLength(50),

                        TextInput::make('link')
                            ->label('Tautan')
                            ->placeholder('https://www.pwknews.com')
                            ->autocomplete(false)
                            ->nullable()
                            ->maxLength(255)
                            ->url(),

                        Textarea::make('address')
                            ->label('Alamat')
                            ->placeholder('Jl. Kebontoro, Jakarta Pusat')
                            ->autocomplete(false)
                            ->required()
                            ->columnSpanFull(),

                        Radio::make('type')
                            ->label('Jenis')
                            ->options(MediaType::options())
                            ->inline()
                            ->required()
                            ->in(implode(',', array_column(MediaType::cases(), 'value'))),

                        Radio::make('classification')
                            ->label('Klasifikasi')
                            ->options(MediaClassification::options())
                            ->inline()
                            ->required()
                            ->in(implode(',', array_column(MediaClassification::cases(), 'value'))),
                    ])
                    ->columns(2)
                    ->columnSpan(2),

                Group::make()
                    ->schema(
                        collect($documents)
                            ->map(function (array $doc): Section {
                                return Section::make($doc['title'])
                                    ->schema([
                                        TextInput::make($doc['text_name'])
                                            ->hiddenLabel()
                                            ->placeholder($doc['placeholder'])
                                            ->autocomplete(false)
                                            ->required()
                                            ->maxLength($doc['max']),

                                        AdvancedFileUpload::make($doc['file_name'])
                                            ->hiddenLabel()
                                            ->disk(config('filesystems.default'))
                                            ->acceptedFileTypes($doc['accept'])
                                            ->maxSize($doc['max_size'])
                                            ->directory($doc['folder'].now()->toDateString()),
                                    ]);
                            })->toArray()
                    )
                    ->columns(2),
            ])
            ->statePath('data');
    }

    public function save(): void
    {
        $this->mountAction('save');
    }

    public function saveAction(): Action
    {
        $needsReview = $this->media?->company?->verificationRequest?->status !== null;

        return Action::make('save')
            ->label('Simpan')
            ->modalHeading('Ajukan Perubahan')
            ->modal($needsReview)
            ->schema(
                $needsReview
                    ? [
                        Textarea::make('change_reason')
                            ->label('Alasan')
                            ->placeholder('Jelaskan alasan perubahan data ini...')
                            ->autocomplete(false)
                            ->autofocus()
                            ->required(),
                    ]
                    : []
            )
            ->action(function (array $data = []) use ($needsReview) {
                $this->performSave($data, $needsReview);
            })
            ->modalWidth(Width::Large);
    }

    public function performSave(array $data = [], bool $needsReview = false): void
    {
        if ($needsReview) {
            $existingRequest = DataChangeRequest::where('entity_type', PartnerMedia::class)
                ->where('entity_id', $this->media->id)
                ->where('status', DataChangeStatus::PENDING)
                ->exists();

            if ($existingRequest) {
                CheerfulNotification::warning(
                    'Pengajuan Sudah Ada ⏳',
                    'Masih ada pengajuan perubahan data yang sedang menunggu verifikasi. Silakan tunggu hingga selesai diproses.'
                )
                    ->send();

                return;
            }

            $state = $this->form->getState();
            $data = array_merge($state, $data);

            $fieldLabels = PartnerMedia::dataChangeEntryLabels();

            $editableFields = [
                'name',
                'link',
                'address',
                'type',
                'classification',
                'journalism_organization',
                'press_council_certificate',
            ];

            $changedFields = [];
            $oldDataArr = [];

            $casts = $this->media->getCasts();
            foreach ($editableFields as $field) {
                $val = $data[$field];
                $oldVal = $this->media->{$field};

                // If value is a raw scalar but should be an enum, resolve it for display
                if (isset($casts[$field]) && ! ($val instanceof BackedEnum) && enum_exists($casts[$field])) {
                    $enumClass = $casts[$field];
                    $val = $enumClass::tryFrom($val) ?? $val;
                }

                // Use labels for Enums if they implement HasLabel
                $displayVal = ($val instanceof HasLabel) ? $val->getLabel() : ($val instanceof BackedEnum ? $val->value : $val);
                $displayOldVal = ($oldVal instanceof HasLabel) ? $oldVal->getLabel() : ($oldVal instanceof BackedEnum ? $oldVal->value : $oldVal);

                if ($displayOldVal !== $displayVal) {
                    $label = $fieldLabels[$field] ?? $field;
                    $changedFields[$label] = $displayVal;
                    $oldDataArr[$label] = $displayOldVal;
                }
            }

            $documents = [
                'journalism_organization_docs',
                'press_council_certificate_docs',
            ];

            foreach ($documents as $field) {
                $newValue = $this->data[$field] ?? [];
                $oldValue = $this->originalDocuments[$field] ?? [];

                if (json_encode($newValue) !== json_encode($oldValue)) {
                    $label = $fieldLabels[$field] ?? $field;
                    $changedFields[$label] = $newValue;
                    $oldDataArr[$label] = $oldValue;
                }
            }

            if (empty($changedFields)) {
                CheerfulNotification::info(
                    'Belum ada yang berubah ✨',
                    'Ubah data terlebih dulu lalu simpan kembali 💪'
                )
                    ->send();

                return;
            }

            $dataChangeRequest = DataChangeRequest::create([
                'user_id' => auth()->id(),
                'entity_type' => PartnerMedia::class,
                'entity_id' => $this->media->id,
                'old_data' => $oldDataArr,
                'new_data' => $changedFields,
                'change_reason' => $data['change_reason'],
            ]);

            CheerfulNotification::success(
                'Permohonan Berhasil 🎉',
                'Data sudah dikirim dan sedang menunggu verifikasi admin ⏳'
            )
                ->send();

            User::superAdmin()
                ->get()
                ->each(function ($admin) use ($dataChangeRequest): void {
                    $admin->notify(new BroadcastNotification([
                        'title' => 'Ada Pengajuan Perubahan Data Media ✨',
                        'body' => 'Halooo Admin! 👋 '.auth()->user()->name.' baru saja mengajukan perubahan data media. Yuk, cek detailnya! 🚀',
                        'action' => [
                            Action::make('view')
                                ->label('Lihat')
                                ->url(DataChangesResource::getUrl('view', ['record' => $dataChangeRequest->id])),
                        ],
                    ]));
                });
        } else {
            $state = $this->form->getState();

            $media = PartnerMedia::updateOrCreate([
                'id' => $this->media?->id,
            ], [
                'company_id' => $this->company->id,
                'name' => $state['name'],
                'link' => $state['link'],
                'address' => $state['address'],
                'type' => $state['type'],
                'classification' => $state['classification'],
                'journalism_organization' => $state['journalism_organization'],
                'press_council_certificate' => $state['press_council_certificate'],
            ]);

            $this->media = $media;

            $documents = [
                'journalism_organization_docs',
                'press_council_certificate_docs',
            ];

            foreach ($documents as $field) {
                if (empty($this->data[$field])) {
                    continue;
                }

                foreach ((array) $this->data[$field] as $filePath) {
                    $fullPath = Storage::disk(config('filesystems.default'))->path($filePath);

                    if (! file_exists($fullPath)) {
                        continue;
                    }

                    $media
                        ->addMediaFromDisk($filePath, config('filesystems.default'))
                        ->preservingOriginal()
                        ->withCustomProperties([
                            'feature' => 'media',
                            'date' => now()->toDateString(),
                            'doc_type' => str($field)
                                ->replace('_docs', '')
                                ->slug('-'),
                        ])
                        ->toMediaCollection('partnerMedia');
                }
            }

            CheerfulNotification::create()->send();
        }
    }
}
