<?php

namespace App\Services;

use App\Models\Testimonial;
use App\Models\TestimonialTranslation;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;

class TestimonialTranslationSyncService
{
    public function __construct(
        private readonly FranchiseGlobalSourceResolver $franchiseGlobalSourceResolver,
    ) {
    }

    public const STATE_NOT_ADOPTED = 'not_adopted';
    public const STATE_UP_TO_DATE = 'up_to_date';
    public const STATE_OUTDATED = 'outdated';
    public const STATE_LOCALLY_MODIFIED = 'locally_modified';

    private const TRANSLATABLE_FIELDS = ['parent_name', 'role', 'comment', 'status'];

    public function saveGlobalTranslation(Testimonial $testimonial, string $language, array $attributes): TestimonialTranslation
    {
        return DB::transaction(function () use ($testimonial, $language, $attributes) {
            $translation = TestimonialTranslation::firstOrNew([
                'testimonial_id' => $testimonial->id,
                'language' => strtolower($language),
            ]);

            $dirty = $translation->exists ? $this->hasTranslatableChanges($translation, $attributes) : true;
            $translation->fill(Arr::only($attributes, self::TRANSLATABLE_FIELDS));

            if (!$translation->exists) {
                $translation->version = 1;
            } elseif ($dirty) {
                $translation->version = max(1, (int) $translation->version) + 1;
            }

            $translation->save();
            return $translation;
        });
    }

    public function saveFranchiseManualTranslation(Testimonial $testimonial, string $language, array $attributes): TestimonialTranslation
    {
        return DB::transaction(function () use ($testimonial, $language, $attributes) {
            $translation = TestimonialTranslation::firstOrNew([
                'testimonial_id' => $testimonial->id,
                'language' => strtolower($language),
            ]);

            $globalTranslation = !$testimonial->is_global && $testimonial->parent_testimonials_id
                ? $this->resolveGlobalTranslationForFranchisePreferred($testimonial, $language)
                : null;

            if (!$translation->exists && $globalTranslation) {
                $translation->fill(Arr::only($globalTranslation->toArray(), self::TRANSLATABLE_FIELDS));
                $translation->adopted_from_global = true;
                $translation->source_global_translation_id = $globalTranslation->id;
                $translation->source_global_version = (int) ($globalTranslation->version ?? 1);
                $translation->version = 1;
            }

            $dirty = $translation->exists ? $this->hasTranslatableChanges($translation, $attributes) : true;
            $translation->fill(Arr::only($attributes, self::TRANSLATABLE_FIELDS));

            if (!$translation->exists) {
                $translation->version = 1;
            }

            if ($dirty) {
                $translation->last_local_edit_at = Carbon::now();
            }

            $translation->save();
            return $translation;
        });
    }

    public function adoptGlobal(Testimonial $targetTestimonial, string $language): TestimonialTranslation
    {
        return $this->copyGlobalIntoFranchise($targetTestimonial, $language);
    }

    public function resetToGlobal(Testimonial $targetTestimonial, string $language): TestimonialTranslation
    {
        return $this->copyGlobalIntoFranchise($targetTestimonial, $language);
    }

    public function updateToLatestGlobal(Testimonial $targetTestimonial, string $language): TestimonialTranslation
    {
        return $this->copyGlobalIntoFranchise($targetTestimonial, $language);
    }

    public function syncState(?TestimonialTranslation $franchiseTranslation, ?TestimonialTranslation $globalTranslation): string
    {
        if (!$franchiseTranslation || !$franchiseTranslation->adopted_from_global || !$franchiseTranslation->source_global_translation_id) {
            return self::STATE_NOT_ADOPTED;
        }
        if (!$globalTranslation) {
            return self::STATE_NOT_ADOPTED;
        }
        $sourceVersion = (int) ($franchiseTranslation->source_global_version ?? 0);
        $globalVersion = (int) ($globalTranslation->version ?? 1);

        if ($sourceVersion < $globalVersion && !is_null($franchiseTranslation->last_local_edit_at)) {
            return self::STATE_LOCALLY_MODIFIED;
        }
        if ($sourceVersion < $globalVersion) {
            return self::STATE_OUTDATED;
        }

        return self::STATE_UP_TO_DATE;
    }

    public function hasUpdateAvailable(?TestimonialTranslation $franchiseTranslation, ?TestimonialTranslation $globalTranslation): bool
    {
        if (!$franchiseTranslation || !$globalTranslation) {
            return false;
        }

        if (empty($franchiseTranslation->source_global_translation_id)) {
            return false;
        }

        return (int) ($franchiseTranslation->source_global_version ?? 0) < (int) ($globalTranslation->version ?? 1);
    }

    public function resolveGlobalTranslationForFranchisePreferred(Testimonial $testimonial, string $franchisePreferredLanguage): ?TestimonialTranslation
    {
        $code = $this->franchiseGlobalSourceResolver->effectiveGlobalSourceLanguage($franchisePreferredLanguage);

        return $this->resolveGlobalTranslation($testimonial, $code);
    }

    public function resolveGlobalTranslation(Testimonial $testimonial, string $language): ?TestimonialTranslation
    {
        $globalId = $testimonial->is_global ? $testimonial->id : ($testimonial->parent_testimonials_id ?: $testimonial->id);

        $translation = TestimonialTranslation::where('testimonial_id', $globalId)->byLanguage($language)->published()->first();
        if ($translation) return $translation;

        return TestimonialTranslation::where('testimonial_id', $globalId)->byLanguage('en')->published()->first();
    }

    private function copyGlobalIntoFranchise(Testimonial $targetTestimonial, string $language): TestimonialTranslation
    {
        return DB::transaction(function () use ($targetTestimonial, $language) {
            $franchiseRowLanguage = strtolower($language);
            $sourceLanguage = $this->franchiseGlobalSourceResolver->effectiveGlobalSourceLanguage($language);
            $globalTranslation = $this->resolveGlobalTranslation($targetTestimonial, $sourceLanguage);
            if (!$globalTranslation) {
                throw new \RuntimeException('Global translation not found for requested language.');
            }

            $franchiseTranslation = TestimonialTranslation::firstOrNew([
                'testimonial_id' => $targetTestimonial->id,
                'language' => $franchiseRowLanguage,
            ]);

            $franchiseTranslation->fill(Arr::only($globalTranslation->toArray(), self::TRANSLATABLE_FIELDS));
            $franchiseTranslation->adopted_from_global = true;
            $franchiseTranslation->source_global_translation_id = $globalTranslation->id;
            $franchiseTranslation->source_global_version = (int) ($globalTranslation->version ?? 1);
            $franchiseTranslation->last_local_edit_at = null;
            $franchiseTranslation->version = max(1, (int) ($franchiseTranslation->version ?? 1));
            $franchiseTranslation->save();

            return $franchiseTranslation;
        });
    }

    private function hasTranslatableChanges(TestimonialTranslation $existing, array $incoming): bool
    {
        foreach (self::TRANSLATABLE_FIELDS as $field) {
            if (array_key_exists($field, $incoming) && $existing->{$field} != $incoming[$field]) {
                return true;
            }
        }
        return false;
    }
}
