<?php

namespace App\Filament\Pages\Media\Services;

use App\Enums\DataChangeStatus;
use App\Filament\Resources\Manage\DataChanges\DataChangesResource;
use App\Filament\Support\CheerfulNotification;
use App\Models\DataChangeRequest;
use App\Models\PartnerMedia;
use App\Models\User;
use App\Notifications\BroadcastNotification;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Support\Contracts\HasLabel;
use Illuminate\Support\Facades\Storage;
use UnitEnum;

class MediaSaveService
{
    public static function handle(
        ?PartnerMedia $existingMedia,
        int $companyId,
        array $formState,
        array $pageData,
        array $originalDocuments,
        bool $needsReview,
        array $actionData = [],
    ): ?PartnerMedia {
        if ($needsReview && $existingMedia) {
            self::createDataChangeRequest(
                media: $existingMedia,
                formState: $formState,
                pageData: $pageData,
                originalDocuments: $originalDocuments,
                actionData: $actionData,
            );

            return $existingMedia;
        }

        return self::saveMediaDirectly(
            existingMedia: $existingMedia,
            companyId: $companyId,
            formState: $formState,
            pageData: $pageData,
        );
    }

    private static function createDataChangeRequest(
        PartnerMedia $media,
        array $formState,
        array $pageData,
        array $originalDocuments,
        array $actionData,
    ): void {
        $existingRequest = DataChangeRequest::where('entity_type', PartnerMedia::class)
            ->where('entity_id', $media->id)
            ->where('status', DataChangeStatus::PENDING)
            ->exists();

        if ($existingRequest) {
            CheerfulNotification::warning(
                CheerfulNotification::getByKey('data_change.existing.title'),
                CheerfulNotification::getByKey('data_change.existing.body')
            )->send();

            return;
        }

        $data = array_merge($formState, $actionData);

        $fieldLabels = PartnerMedia::dataChangeEntryLabels();

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

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

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

            if (isset($casts[$field]) && ! ($val instanceof BackedEnum) && enum_exists($casts[$field])) {
                /** @var class-string<UnitEnum> $enumClass */
                $enumClass = $casts[$field];
                $val = $enumClass::tryFrom($val) ?? $val;
            }

            $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 = $pageData[$field] ?? [];
            $oldValue = $originalDocuments[$field] ?? [];

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

        if (empty($changedFields)) {
            CheerfulNotification::info(
                CheerfulNotification::getByKey('data_change.no_change.title'),
                CheerfulNotification::getByKey('data_change.no_change.body')
            )->send();

            return;
        }

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

        CheerfulNotification::success(
            CheerfulNotification::getByKey('data_change.success.title'),
            CheerfulNotification::getByKey('data_change.success.body')
        )->send();

        User::superAdmin()
            ->get()
            ->each(function ($admin) use ($dataChangeRequest): void {
                $admin->notify(new BroadcastNotification([
                    'title' => CheerfulNotification::getByKey('data_change.admin_notify.title'),
                    'body' => CheerfulNotification::getByKey('data_change.admin_notify.body', ['name' => auth()->user()->name]),
                    'action' => [
                        Action::make('view')
                            ->label('Lihat')
                            ->url(DataChangesResource::getUrl('view', ['record' => $dataChangeRequest->id])),
                    ],
                ]));
            });
    }

    private static function saveMediaDirectly(
        ?PartnerMedia $existingMedia,
        int $companyId,
        array $formState,
        array $pageData,
    ): PartnerMedia {
        $media = PartnerMedia::updateOrCreate([
            'id' => $existingMedia?->id,
        ], [
            'company_id' => $companyId,
            'name' => $formState['name'],
            'link' => $formState['link'],
            'address' => $formState['address'],
            'type' => $formState['type'],
            'classification' => $formState['classification'],
            'journalism_organization' => $formState['journalism_organization'],
            'press_council_certificate' => $formState['press_council_certificate'],
        ]);

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

        foreach ($documents as $field) {
            if (empty($pageData[$field])) {
                continue;
            }

            foreach ((array) $pageData[$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');
            }
        }

        return $media;
    }
}
