<?php

namespace App\Http\Controllers;

use App\Models\Label;
use App\Models\Song;
use App\Models\SongArrangementSection;
use App\Models\SongSection;
use App\Support\CcliLabels;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class SongSectionController extends Controller
{
    private const DEFAULT_LABEL_COLOR = '#3B82F6';

    public function __construct(
        private readonly SongController $songController,
    ) {}

    public function store(Request $request, Song $song): JsonResponse
    {
        $data = $request->validate([
            'label_name' => ['required', 'string', 'max:255'],
            'color' => ['nullable', 'string', 'regex:/^#[0-9A-Fa-f]{6}$/'],
            'slides' => ['array'],
            'slides.*.text_content' => ['required', 'string'],
            'slides.*.text_content_translated' => ['nullable', 'string'],
        ], $this->validationMessages());

        $normalizedLabelName = CcliLabels::normalizeLabelName($data['label_name']);

        $responseSong = DB::transaction(function () use ($song, $data, $normalizedLabelName): Song {
            $label = Label::firstOrCreate(
                ['name' => $normalizedLabelName],
                ['color' => $data['color'] ?? self::DEFAULT_LABEL_COLOR],
            );

            if ($song->sections()->where('label_id', $label->id)->exists()) {
                abort(response()->json([
                    'message' => 'Dieser Abschnitt existiert bereits in diesem Lied.',
                ], 422));
            }

            $section = $song->sections()->create([
                'label_id' => $label->id,
                'order' => ((int) $song->sections()->max('order')) + 1,
            ]);

            $this->replaceSlides($section, $data['slides'] ?? []);

            $defaultArrangement = $song->arrangements()->firstOrCreate(
                ['is_default' => true],
                ['name' => 'Normal'],
            );

            $defaultArrangement->arrangementSections()->create([
                'song_section_id' => $section->id,
                'order' => ((int) $defaultArrangement->arrangementSections()->max('order')) + 1,
            ]);

            $this->recomputeHasTranslation($song);

            return $this->freshSong($song);
        });

        return response()->json([
            'message' => 'Sektion wurde hinzugefügt.',
            'data' => $this->songController->formatSongDetail($responseSong),
        ], 201);
    }

    public function update(Request $request, Song $song, SongSection $section): JsonResponse
    {
        if ((int) $section->song_id !== (int) $song->id) {
            return response()->json(['message' => 'Sektion nicht gefunden.'], 404);
        }

        if ($section->locked) {
            return response()->json(['message' => 'Diese Sektion ist gesperrt und kann nicht bearbeitet werden.'], 422);
        }

        $data = $request->validate([
            'slides' => ['required', 'array'],
            'slides.*.text_content' => ['required', 'string'],
            'slides.*.text_content_translated' => ['nullable', 'string'],
            'order' => ['sometimes', 'integer'],
            'label_name' => ['sometimes', 'string', 'max:255'],
            'color' => ['sometimes', 'nullable', 'string', 'regex:/^#[0-9A-Fa-f]{6}$/'],
        ], $this->validationMessages());

        $responseSong = DB::transaction(function () use ($song, $section, $data): Song {
            if (array_key_exists('order', $data)) {
                $section->update(['order' => $data['order']]);
            }

            if (array_key_exists('label_name', $data) && trim($data['label_name']) !== '') {
                $normalizedLabelName = CcliLabels::normalizeLabelName($data['label_name']);

                $label = Label::firstOrCreate(
                    ['name' => $normalizedLabelName],
                    ['color' => $data['color'] ?? self::DEFAULT_LABEL_COLOR],
                );

                $section->update(['label_id' => $label->id]);
            }

            $this->replaceSlides($section, $data['slides']);
            $this->recomputeHasTranslation($song);

            return $this->freshSong($song);
        });

        return response()->json([
            'message' => 'Sektion wurde gespeichert.',
            'data' => $this->songController->formatSongDetail($responseSong),
        ]);
    }

    public function destroy(Song $song, SongSection $section): JsonResponse
    {
        if ((int) $section->song_id !== (int) $song->id) {
            return response()->json(['message' => 'Sektion nicht gefunden.'], 404);
        }

        if ($section->locked) {
            return response()->json(['message' => 'Diese Sektion ist gesperrt und kann nicht gelöscht werden.'], 422);
        }

        $responseSong = DB::transaction(function () use ($song, $section): Song {
            SongArrangementSection::query()
                ->where('song_section_id', $section->id)
                ->whereHas('arrangement', fn ($query) => $query->where('song_id', $song->id))
                ->delete();

            $section->slides()->delete();
            $section->delete();

            $this->recomputeHasTranslation($song);

            return $this->freshSong($song);
        });

        return response()->json([
            'message' => 'Sektion wurde gelöscht.',
            'data' => $this->songController->formatSongDetail($responseSong),
        ]);
    }

    private function replaceSlides(SongSection $section, array $slides): void
    {
        $section->slides()->delete();

        foreach (array_values($slides) as $index => $slide) {
            $section->slides()->create([
                'order' => $index + 1,
                'text_content' => $slide['text_content'],
                'text_content_translated' => $slide['text_content_translated'] ?? null,
            ]);
        }
    }

    private function recomputeHasTranslation(Song $song): void
    {
        $hasTranslation = $song->sections()
            ->whereHas('slides', fn ($query) => $query
                ->whereNotNull('text_content_translated')
                ->where('text_content_translated', '!=', ''))
            ->exists();

        $song->update(['has_translation' => $hasTranslation]);
    }

    private function freshSong(Song $song): Song
    {
        return $song->fresh(['arrangements.arrangementSections.section.slides', 'arrangements.arrangementSections.section.label']);
    }

    private function validationMessages(): array
    {
        return [
            'label_name.required' => 'Bitte gib einen Namen für die Sektion ein.',
            'label_name.string' => 'Der Sektionsname muss ein Text sein.',
            'label_name.max' => 'Der Sektionsname darf höchstens 255 Zeichen lang sein.',
            'color.regex' => 'Bitte gib eine gültige Hex-Farbe an.',
            'slides.required' => 'Bitte gib mindestens eine Folie an.',
            'slides.array' => 'Die Folien müssen als Liste gesendet werden.',
            'slides.*.text_content.required' => 'Bitte gib einen Text für jede Folie ein.',
            'slides.*.text_content.string' => 'Der Folientext muss ein Text sein.',
            'slides.*.text_content_translated.string' => 'Der übersetzte Folientext muss ein Text sein.',
            'order.integer' => 'Die Reihenfolge muss eine Zahl sein.',
        ];
    }
}
