<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Repositories\AppointmentRepository;
use App\Repositories\InstructorRepository;
use App\Repositories\ReferenceDataRepository;
use App\Repositories\StudentRepository;
use App\Services\CalendarService;
use App\Services\RequirementService;
use App\Support\Auth;
use App\Support\Request;
use App\Support\Response;

final class AppointmentsController
{
    public function index(Request $request): void
    {
        $user = Auth::requireUser();
        $from = (string) $request->query('from');
        $to = (string) $request->query('to');
        $instructorId = null;
        if ($user['role'] === 'instructor') {
            $instructor = (new InstructorRepository())->findByUserId((int) $user['tenant_id'], (int) $user['id']);
            $instructorId = $instructor ? (int) $instructor['id'] : null;
        }
        $data = (new AppointmentRepository())->listForTenant(
            (int) $user['tenant_id'],
            $from,
            $to,
            $instructorId
        );
        Response::json(['data' => $data]);
    }

    public function validate(Request $request): void
    {
        $user = Auth::requireUser();
        $payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
        $warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload);
        Response::json(['warnings' => $warnings, 'payload' => $payload]);
    }

    public function store(Request $request): void
    {
        $user = Auth::requireRole(['tenant_admin', 'dispatcher', 'instructor']);
        $payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
        $warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload);

        if ($this->hasWarnings($warnings) && empty($payload['warning_acknowledged'])) {
            Response::json(['message' => 'Warnings require acknowledgement', 'warnings' => $warnings], 409);
        }

        $record = (new AppointmentRepository())->create((int) $user['tenant_id'], $payload, (int) $user['id']);
        Response::json([
            'data' => $record,
            'warnings' => $warnings,
            'progress' => $payload['student_id'] ? (new RequirementService())->progressForStudent((int) $payload['student_id']) : [],
        ], 201);
    }

    public function update(Request $request, array $params): void
    {
        $user = Auth::requireRole(['tenant_admin', 'dispatcher', 'instructor']);
        $payload = $this->normalizePayload((int) $user['tenant_id'], $request->input());
        $warnings = (new CalendarService())->validate((int) $user['tenant_id'], $payload, (int) $params['id']);

        if ($this->hasWarnings($warnings) && empty($payload['warning_acknowledged'])) {
            Response::json(['message' => 'Warnings require acknowledgement', 'warnings' => $warnings], 409);
        }

        $record = (new AppointmentRepository())->update((int) $user['tenant_id'], (int) $params['id'], $payload, (int) $user['id']);
        Response::json([
            'data' => $record,
            'warnings' => $warnings,
            'progress' => $payload['student_id'] ? (new RequirementService())->progressForStudent((int) $payload['student_id']) : [],
        ]);
    }

    public function destroy(Request $request, array $params): void
    {
        $user = Auth::requireRole(['tenant_admin', 'dispatcher']);
        (new AppointmentRepository())->delete((int) $user['tenant_id'], (int) $params['id']);
        Response::noContent();
    }

    private function normalizePayload(int $tenantId, array $payload): array
    {
        $referenceRepository = new ReferenceDataRepository();
        $lessonType = $referenceRepository->findLessonType($tenantId, (int) $payload['lesson_type_id']);
        if ($lessonType === null) {
            Response::json(['message' => 'Unbekannte Stundenart'], 422);
        }

        $student = null;
        if (!empty($payload['student_id'])) {
            $student = (new StudentRepository())->find($tenantId, (int) $payload['student_id']);
            if ($student === null) {
                Response::json(['message' => 'Unbekannter Fahrschueler'], 422);
            }

            if (
                $lessonType['category'] === 'student'
                && !$referenceRepository->isLessonTypeVisibleForTemplate($tenantId, (int) $lessonType['id'], (int) $student['template_id'])
            ) {
                Response::json(['message' => 'Diese Stundenart ist fuer die Fuehrerscheinklasse nicht freigegeben'], 422);
            }
        }

        $startAt = new \DateTimeImmutable((string) $payload['start_at']);
        $baseDuration = max(1, (int) $lessonType['default_duration']);
        $providedEndAt = !empty($payload['end_at']) ? new \DateTimeImmutable((string) $payload['end_at']) : null;

        if ($providedEndAt instanceof \DateTimeImmutable && $providedEndAt > $startAt) {
            $endAt = $providedEndAt;
            $duration = max(1, (int) round(($endAt->getTimestamp() - $startAt->getTimestamp()) / 60));
            $units = (int) $lessonType['fixed_duration'] === 1
                ? 1
                : max(1, (int) round($duration / $baseDuration));
        } else {
            $units = max(1, (int) ($payload['units'] ?? 1));
            $duration = (int) $lessonType['fixed_duration'] === 1
                ? $baseDuration
                : $baseDuration * $units;
            $endAt = $startAt->modify(sprintf('+%d minutes', $duration));
        }

        $title = $payload['title'] ?? $lessonType['name'] ?? 'Termin';
        $unitsBreakdown = [];

        if ($lessonType && (int) $lessonType['is_counted'] === 1 && !empty($payload['student_id'])) {
            $requirementKey = $lessonType['effective_requirement_key'] ?? $lessonType['requirement_key'];
            if ($student !== null && (int) ($student['template_is_combination'] ?? 0) === 1) {
                $requirementKey = $this->requirementKeyForPhase($student['requirements'] ?? [], $requirementKey, (string) ($payload['requirement_phase'] ?? ''));
            }

            $unitsBreakdown[] = [
                'requirement_key' => $requirementKey,
                'label' => $lessonType['name'],
                'units_counted' => $units,
            ];
        }

        return [
            'student_id' => $payload['student_id'] ?? null,
            'instructor_id' => (int) $payload['instructor_id'],
            'lesson_type_id' => (int) $payload['lesson_type_id'],
            'title' => $title,
            'category' => $lessonType['category'] ?? ($payload['category'] ?? 'student'),
            'start_at' => $startAt->format(DATE_ATOM),
            'end_at' => $endAt->format(DATE_ATOM),
            'units' => $units,
            'status' => $payload['status'] ?? 'planned',
            'notes' => $payload['notes'] ?? '',
            'warning_acknowledged' => !empty($payload['warning_acknowledged']),
            'units_breakdown' => $unitsBreakdown,
        ];
    }

    private function hasWarnings(array $warnings): bool
    {
        return !empty($warnings['conflicts']) || !empty($warnings['rest_period_warnings']);
    }

    private function requirementKeyForPhase(array $requirements, string $baseRequirementKey, string $phaseLabel): string
    {
        $matchingRows = array_values(array_filter(
            $requirements,
            static fn (array $row): bool => str_ends_with((string) $row['requirement_key'], '::' . $baseRequirementKey)
                || (string) $row['requirement_key'] === $baseRequirementKey
        ));

        if ($matchingRows === []) {
            return $baseRequirementKey;
        }

        foreach ($matchingRows as $row) {
            if ((string) ($row['phase_label'] ?? '') === $phaseLabel) {
                return (string) $row['requirement_key'];
            }
        }

        return (string) $matchingRows[0]['requirement_key'];
    }
}
