<?php

namespace App\Domains\Tenant\Services;

use App\Domains\Attachable\Models\Attachment;
use App\Domains\Attachable\Services\AttachmentService;
use App\Domains\Tenant\Models\WebsiteType;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class WebsiteTypeService
{
    public function __construct(
        protected AttachmentService $attachmentService,
    ) {}

    /**
     * Create a website type and store its logos.
     *
     * @param  array<string, mixed>  $data
     */
    public function create(array $data): WebsiteType
    {
        return $this->save(new WebsiteType, $data);
    }

    /**
     * Create or update a website type and replace its logos when provided.
     *
     * @param  array<string, mixed>  $attributes
     * @param  array<string, mixed>  $values
     */
    public function updateOrCreate(array $attributes, array $values = []): WebsiteType
    {
        /** @var WebsiteType $websiteType */
        $websiteType = WebsiteType::query()->firstOrNew($attributes);

        return $this->save($websiteType, [...$attributes, ...$values]);
    }

    /**
     * @param  array<string, mixed>  $data
     */
    private function save(WebsiteType $websiteType, array $data): WebsiteType
    {
        return DB::transaction(function () use ($websiteType, $data): WebsiteType {
            $previousLogos = [];

            $attachmentFields = [
                'site_logo' => ['relation' => 'siteLogo', 'column' => 'site_logo'],
                'footer_logo' => ['relation' => 'footerLogo', 'column' => 'footer_logo'],
                'favicon' => ['relation' => 'favicon', 'column' => 'favicon_id'],
            ];

            foreach ($attachmentFields as $field => $attachmentField) {
                if (! array_key_exists($field, $data)) {
                    continue;
                }

                $logo = $data[$field];
                $previousLogos[$field] = $websiteType->exists
                    ? $websiteType->{$attachmentField['relation']}()->first()
                    : null;
                unset($data[$field]);

                $attachment = null;

                if ($logo) {
                    $attachment = is_string($logo) && Str::isUuid($logo)
                        ? Attachment::query()->where('key', $logo)->first()
                        : $this->attachmentService->store($logo, 'website-types');
                }

                $data[$attachmentField['column']] = $attachment?->id;
            }

            $websiteType->fill($data)->save();

            foreach ($previousLogos as $previousLogo) {
                if (
                    $previousLogo instanceof Attachment
                    && $previousLogo->id !== $websiteType->site_logo
                    && $previousLogo->id !== $websiteType->footer_logo
                    && $previousLogo->id !== $websiteType->favicon_id
                    && ! $this->isReferencedByWebsiteType($previousLogo)
                ) {
                    $this->attachmentService->delete($previousLogo);
                }
            }

            return $websiteType;
        });
    }

    private function isReferencedByWebsiteType(Attachment $attachment): bool
    {
        return WebsiteType::query()
            ->where(function ($query) use ($attachment): void {
                $query
                    ->where('site_logo', $attachment->id)
                    ->orWhere('footer_logo', $attachment->id)
                    ->orWhere('favicon_id', $attachment->id);
            })
            ->exists();
    }
}
