<?php

namespace App\Domains\Tenant\Services;

use App\Domains\Attachable\Enums\AttachmentType;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Attachable\Services\AttachmentService;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

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

    /**
     * Create a new tenant and store its logos.
     *
     * @param  array<string, mixed>  $data
     */
    public function create(array $data): Tenant
    {
        return DB::transaction(function () use ($data): Tenant {
            $headerLogo = $data['header_logo'] ?? null;
            $footerLogo = $data['footer_logo'] ?? null;
            $heroBgImage = $data['hero_bg_image'] ?? null;
            $mainCarouselImages = $data['main_carousel_images'] ?? [];
            $socialMedia = $data['social_media'] ?? [];

            unset(
                $data['header_logo'],
                $data['footer_logo'],
                $data['hero_bg_image'],
                $data['main_carousel_images'],
                $data['social_media']
            );

            $headerAttachmentId = null;
            if ($headerLogo) {
                $attachment = Str::isUuid($headerLogo)
                    ? Attachment::query()->where('key', $headerLogo)->first()
                    : $this->attachmentService->store($headerLogo, 'tenants');

                if ($attachment) {
                    $headerAttachmentId = $attachment->id;
                }
            }

            $footerAttachmentId = null;
            if ($footerLogo) {
                $attachment = Str::isUuid($footerLogo)
                    ? Attachment::query()->where('key', $footerLogo)->first()
                    : $this->attachmentService->store($footerLogo, 'tenants');

                if ($attachment) {
                    $footerAttachmentId = $attachment->id;
                }
            }

            $data['header_logo_id'] = $headerAttachmentId;
            $data['footer_logo_id'] = $footerAttachmentId;

            if ($heroBgImage) {
                $attachment = Str::isUuid($heroBgImage)
                    ? Attachment::query()->where('key', $heroBgImage)->first()
                    : $this->attachmentService->store($heroBgImage, 'tenants');

                if ($attachment) {
                    $heroConfig = $data['hero_config'] ?? [];
                    $heroConfig['background_image_id'] = $attachment->id;
                    $data['hero_config'] = $heroConfig;
                }
            }

            /** @var Tenant $tenant */
            $tenant = Tenant::query()->create($data);
            $this->syncMainCarouselImages($tenant, $mainCarouselImages);
            $this->syncSocialMedia($tenant, $socialMedia);

            return $tenant;
        });
    }

    /**
     * Update an existing tenant and store new logos if uploaded.
     *
     * @param  array<string, mixed>  $data
     */
    public function update(Tenant $tenant, array $data): Tenant
    {
        return DB::transaction(function () use ($tenant, $data): Tenant {
            $hasHeaderLogoKey = array_key_exists('header_logo', $data);
            $hasFooterLogoKey = array_key_exists('footer_logo', $data);
            $hasHeroBgImageKey = array_key_exists('hero_bg_image', $data);
            $hasMainCarouselImagesKey = array_key_exists('main_carousel_images', $data);
            $hasSocialMediaKey = array_key_exists('social_media', $data);
            $headerLogo = $data['header_logo'] ?? null;
            $footerLogo = $data['footer_logo'] ?? null;
            $heroBgImage = $data['hero_bg_image'] ?? null;
            $mainCarouselImages = $data['main_carousel_images'] ?? [];
            $socialMedia = $data['social_media'] ?? [];

            unset(
                $data['header_logo'],
                $data['footer_logo'],
                $data['hero_bg_image'],
                $data['main_carousel_images'],
                $data['social_media']
            );

            $oldHeroBgId = $tenant->hero_bg_image_id;

            $tenant->fill($data);

            if ($hasHeaderLogoKey) {
                if ($headerLogo) {
                    $attachment = Str::isUuid($headerLogo)
                        ? Attachment::query()->where('key', $headerLogo)->first()
                        : $this->attachmentService->store($headerLogo, 'tenants');

                    if ($attachment) {
                        $tenant->header_logo_id = $attachment->id;
                    } else {
                        $tenant->header_logo_id = null;
                    }
                } else {
                    $tenant->header_logo_id = null;
                }
            }

            if ($hasFooterLogoKey) {
                if ($footerLogo) {
                    $attachment = Str::isUuid($footerLogo)
                        ? Attachment::query()->where('key', $footerLogo)->first()
                        : $this->attachmentService->store($footerLogo, 'tenants');

                    if ($attachment) {
                        $tenant->footer_logo_id = $attachment->id;
                    } else {
                        $tenant->footer_logo_id = null;
                    }
                } else {
                    $tenant->footer_logo_id = null;
                }
            }

            $currentHeroConfig = $tenant->hero_config ?? [];
            if ($hasHeroBgImageKey) {
                if ($heroBgImage) {
                    $attachment = Str::isUuid($heroBgImage)
                        ? Attachment::query()->where('key', $heroBgImage)->first()
                        : $this->attachmentService->store($heroBgImage, 'tenants');

                    if ($attachment) {
                        $currentHeroConfig['background_image_id'] = $attachment->id;
                    } else {
                        unset($currentHeroConfig['background_image_id']);
                    }
                } else {
                    unset($currentHeroConfig['background_image_id']);
                }
            } else {
                if ($oldHeroBgId) {
                    $currentHeroConfig['background_image_id'] = $oldHeroBgId;
                }
            }
            $tenant->hero_config = empty($currentHeroConfig) ? null : $currentHeroConfig;

            $tenant->save();

            if ($hasMainCarouselImagesKey) {
                $this->syncMainCarouselImages($tenant, $mainCarouselImages);
            }

            if ($hasSocialMediaKey) {
                $this->syncSocialMedia($tenant, $socialMedia);
            }

            return $tenant;
        });
    }

    /**
     * @param  array<int, mixed>  $images
     */
    private function syncMainCarouselImages(Tenant $tenant, array $images): void
    {
        $attachments = [];

        foreach (array_values($images) as $order => $image) {
            $attachment = $this->resolveMainCarouselImage($image, $order);
            $attachments[$attachment->id] = ['orden' => $order];
        }

        $tenant->mainCarouselImages()->sync($attachments);
    }

    /**
     * @param  array<int, array{code: string, url: string, orden?: int}>  $socialMedia
     */
    private function syncSocialMedia(Tenant $tenant, array $socialMedia): void
    {
        $associations = [];

        foreach (array_values($socialMedia) as $index => $item) {
            $associations[$item['code']] = [
                'url' => $item['url'],
                'orden' => $item['orden'] ?? $index,
            ];
        }

        $tenant->socialMedia()->sync($associations);
        $tenant->unsetRelation('socialMedia');
    }

    private function resolveMainCarouselImage(mixed $image, int $order): Attachment
    {
        if (is_string($image) && Str::isUuid($image)) {
            $attachment = Attachment::query()
                ->where('key', $image)
                ->where('type', AttachmentType::Image->value)
                ->first();

            if ($attachment === null) {
                throw ValidationException::withMessages([
                    "main_carousel_images.{$order}" => [
                        'La imagen indicada no existe o no es un attachment de tipo imagen.',
                    ],
                ]);
            }

            return $attachment;
        }

        return $this->attachmentService->store($image, 'tenants/main-carousel');
    }
}
