import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncfusion_flutter_calendar/calendar.dart';

import '../../../../api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../../../extensions/date_time.dart';
import '../../../../extensions/text.dart';
import '../../../../routing/app_routes.dart';
import '../../../../state/app/modules/timetable/bloc/timetable_bloc.dart';
import '../../../../state/app/modules/timetable/bloc/timetable_state.dart';
import '../../../../widget/debug/debug_tile.dart';
import '../../../../widget/details_bottom_sheet.dart';
import '../data/lesson_type_label.dart';
import '../subject_colors/subject_color_picker.dart';

class LessonSheet {
  static void show(
    BuildContext context,
    TimetableState? state,
    Appointment appointment,
    McTimetableEntry lesson, {
    bool canEditSubjectColor = false,
  }) {
    if (state == null) return;

    // In Fremdansichten würde der Farb-Button die eigene, globale Fach-Farbe
    // ändern, ohne dass sich der fremde Plan aktualisiert — daher nur im
    // eigenen Plan anbieten. Die Einfärbung (eigene Farben) bleibt.
    final bloc = context.read<TimetableBloc>();
    final subjectShort = lesson.subjects.firstOrNull;
    final subjectEntry = subjectShort == null
        ? null
        : state.subjects?.result
              .where((s) => s.shortName == subjectShort)
              .firstOrNull;
    final headerLong = subjectEntry?.longName;
    // Bei Stunden ohne Fach (Pausenaufsicht etc.) den Lesson-Type-Titel
    // einsetzen — sonst stünde im Header nur ein generisches "?".
    final headerTitle = subjectShort != null
        ? firstNonEmpty([subjectShort, headerLong, '?'])
        : LessonTypeLabel.forEntry(lesson);
    final headerLongName =
        (headerLong != null && headerLong.isNotEmpty && headerLong != headerTitle)
        ? headerLong
        : '';

    final timeRange = appointment.startTime.timeRangeTo(appointment.endTime);

    showDetailsBottomSheet(
      context,
      header: ListTile(
        leading: Icon(_iconForStatus(lesson.status), size: 32),
        title: Text(
          '${_statusPrefix(lesson.status)}$headerTitle',
          style: const TextStyle(fontWeight: FontWeight.bold),
        ),
        subtitle: Text(
          headerLongName.isNotEmpty ? '$timeRange\n$headerLongName' : timeRange,
        ),
        isThreeLine: headerLongName.isNotEmpty,
        trailing: canEditSubjectColor && subjectShort != null
            ? TimetableColorHeaderButton(
                initialColorName: subjectEntry?.color,
                pickerTitle: (headerLong != null && headerLong.isNotEmpty)
                    ? 'Farbe: $headerLong'
                    : 'Farbe: $subjectShort',
                pickerSubtitle: 'Nur reguläre Stunden werden eingefärbt',
                onSelected: (option) =>
                    bloc.setSubjectColor(subjectShort, option.name),
                onReset: () => bloc.clearSubjectColor(subjectShort),
              )
            : null,
      ),
      children: (_) => <Widget>[
        ListTile(
          leading: const Icon(Icons.notifications_active),
          title: Text('Status: ${_statusLabel(lesson.status)}'),
        ),
        if (lesson.subjects.length > 1)
          _listTile(
            icon: Icons.book_outlined,
            label: 'Fächer',
            entries: lesson.subjects
                .map(
                  (s) => _line(s, longname: _subjectLongName(state.subjects, s)),
                )
                .toList(),
          ),
        _roomTile(context, lesson),
        _teacherTile(context, lesson),
        if (lesson.classNames.isNotEmpty)
          _listTile(
            icon: Icons.people,
            label: lesson.classNames.length == 1 ? 'Klasse' : 'Klassen',
            entries: lesson.classNames.map(_line).toList(),
          ),
        ..._optionalTextTiles(lesson),
        DebugTile(context).jsonData(lesson.toJson()),
      ],
    );
  }

  static Widget _roomTile(BuildContext context, McTimetableEntry lesson) {
    final trailing = IconButton(
      icon: const Icon(Icons.house_outlined),
      tooltip: 'Raumplan öffnen',
      onPressed: () => AppRoutes.openRoomplan(context),
    );

    if (lesson.rooms.isEmpty) {
      return ListTile(
        leading: const Icon(Icons.room),
        title: const Text('Raum: ?'),
        trailing: trailing,
      );
    }

    final entries = lesson.rooms
        .map((name) => (main: _line(name), sub: null as String?))
        .toList();

    return _listTileWithSubs(
      icon: Icons.room,
      label: lesson.rooms.length == 1 ? 'Raum' : 'Räume',
      entries: entries,
      trailing: trailing,
    );
  }

  static Widget _teacherTile(BuildContext context, McTimetableEntry lesson) {
    if (lesson.teachers.isEmpty) {
      return const ListTile(
        leading: Icon(Icons.person),
        title: Text('Lehrkraft: ?'),
      );
    }

    // Webuntis liefert entfallende Lehrkräfte bei Vertretungen teils mehrfach —
    // über die Anzeigewerte deduplizieren.
    final seen = <String>{};
    final teachers = <_TeacherDisplay>[];
    for (final t in lesson.teachers) {
      final display = _TeacherDisplay.from(t);
      if (seen.add(display.dedupKey)) teachers.add(display);
    }

    final label = teachers.length == 1 ? 'Lehrkraft' : 'Lehrkräfte';

    // Einzelne, reguläre Lehrkraft kompakt in der Titelzeile.
    if (teachers.length == 1 && teachers.first.isPlain) {
      return ListTile(
        leading: const Icon(Icons.person),
        title: Text('$label: ${teachers.first.after}'),
      );
    }

    return ListTile(
      leading: const Icon(Icons.person),
      title: Text(label),
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [for (final t in teachers) t.buildRow(context)],
      ),
    );
  }

  static Widget _listTileWithSubs({
    required IconData icon,
    required String label,
    required List<({String main, String? sub})> entries,
    Widget? trailing,
  }) {
    if (entries.length == 1) {
      final e = entries.first;
      return ListTile(
        leading: Icon(icon),
        title: Text('$label: ${e.main}'),
        subtitle: e.sub != null ? Text(e.sub!) : null,
        trailing: trailing,
      );
    }
    return ListTile(
      leading: Icon(icon),
      title: Text(label),
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: entries
            .expand<Widget>(
              (e) => [
                Text(e.main),
                if (e.sub != null)
                  Padding(
                    padding: const EdgeInsets.only(left: 12),
                    child: Text(e.sub!),
                  ),
              ],
            )
            .toList(),
      ),
      trailing: trailing,
    );
  }

  static Widget _listTile({
    required IconData icon,
    required String label,
    required List<String> entries,
    Widget? trailing,
  }) {
    if (entries.length == 1) {
      return ListTile(
        leading: Icon(icon),
        title: Text('$label: ${entries.first}'),
        trailing: trailing,
      );
    }
    return ListTile(
      leading: Icon(icon),
      title: Text(label),
      subtitle: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: entries.map<Widget>(Text.new).toList(),
      ),
      trailing: trailing,
    );
  }

  static List<Widget> _optionalTextTiles(McTimetableEntry lesson) {
    return <Widget?>[
      _textTile(Icons.info_outline, 'Info', lesson.infoText),
      _textTile(Icons.swap_horiz, 'Vertretungstext', lesson.substitutionText),
      _textTile(Icons.subject, 'Stundentext', lesson.lessonText),
      _textTile(
        Icons.category_outlined,
        'Stundentyp',
        _lessonTypeLabel(lesson.lessonType),
      ),
    ].whereType<Widget>().toList();
  }

  /// Marianum-Connect liefert den Stundentyp immer (Default `LESSON`). Den
  /// Standard blenden wir aus — sonst stünde unter jeder regulären Stunde
  /// derselbe Eintrag. Sonderfälle bekommen einen deutschen Klartext.
  static String? _lessonTypeLabel(String type) {
    switch (type) {
      case 'LESSON':
        return null;
      case 'OFFICE_HOUR':
        return 'Sprechstunde';
      case 'STANDBY':
        return 'Bereitschaft';
      case 'BREAK_SUPERVISION':
        return 'Pausenaufsicht';
      case 'EXAM':
        return 'Prüfung';
      default:
        return type;
    }
  }

  static Widget? _textTile(IconData icon, String label, String? value) {
    final text = (value ?? '').trim();
    if (text.isEmpty || text == '-') return null;
    return ListTile(
      leading: Icon(icon),
      title: Text(label),
      subtitle: Text(text),
    );
  }

  static String _line(String name, {String? longname}) {
    final parts = <String>[if (name.isNotEmpty) name else '?'];
    final ln = (longname ?? '').trim();
    if (ln.isNotEmpty && ln != name) parts.add('($ln)');
    return parts.join(' ');
  }

  static String? _subjectLongName(dynamic subjects, String shortName) {
    if (subjects == null) return null;
    final list = subjects.result as Iterable<dynamic>;
    for (final s in list) {
      if (s.shortName == shortName) return s.longName as String?;
    }
    return null;
  }

  static IconData _iconForStatus(String status) {
    switch (status) {
      case 'CANCELLED':
        return Icons.event_busy_outlined;
      case 'IRREGULAR':
        return Icons.swap_horiz;
      default:
        return Icons.school_outlined;
    }
  }

  static String _statusLabel(String status) {
    switch (status) {
      case 'CANCELLED':
        return 'Entfällt';
      case 'IRREGULAR':
        return 'Geändert';
      default:
        return 'Regulär';
    }
  }

  static String _statusPrefix(String status) {
    switch (status) {
      case 'CANCELLED':
        return 'Entfällt: ';
      case 'IRREGULAR':
        return 'Änderung: ';
      default:
        return '';
    }
  }
}

/// Aufbereitete Darstellung einer einzelnen Lehrkraft aus einem
/// Webuntis-Element. Trennt die drei Fälle regulär / Vertretung / Entfall,
/// damit die Ansicht statt eines nackten `?` einen sprechenden Namen zeigt.
class _TeacherDisplay {
  /// Ersetzte bzw. entfallende Lehrkraft — steht vorn und wird durchgestrichen.
  /// Leer bei einer regulären Lehrkraft.
  final String before;

  /// Aktuelle Lehrkraft. Leer bei ersatzlosem Entfall.
  final String after;

  const _TeacherDisplay._({this.before = '', this.after = ''});

  bool get isPlain => before.isEmpty;

  String get dedupKey => '$before|$after';

  factory _TeacherDisplay.from(McTimetableTeacher t) {
    final current = _formatName(t.shortName, t.displayName);
    final original = _formatName(
      t.originalShortName ?? '',
      t.originalDisplayName ?? '',
    );

    // Kein (abweichendes) Original → reguläre Lehrkraft.
    if (original.isEmpty || original == current) {
      return _TeacherDisplay._(after: current.isEmpty ? '?' : current);
    }
    // Original vorhanden → ersetzte/entfallende Lehrkraft vorn, aktuelle
    // Lehrkraft (falls vorhanden) als Ersatz dahinter.
    return _TeacherDisplay._(before: original, after: current);
  }

  /// Eine Bullet-Zeile je Lehrkraft: ersetzte Person durchgestrichen, bei
  /// Ersatz ein Pfeil auf die neue Person. Bricht bei Bedarf sauber um.
  Widget buildRow(BuildContext context) {
    final muted = Theme.of(context).colorScheme.onSurfaceVariant;
    final spans = <InlineSpan>[];
    if (before.isNotEmpty) {
      spans.add(
        TextSpan(
          text: before,
          style: TextStyle(
            decoration: TextDecoration.lineThrough,
            color: muted,
          ),
        ),
      );
    }
    if (after.isNotEmpty) {
      if (before.isNotEmpty) {
        spans.add(TextSpan(text: '  →  ', style: TextStyle(color: muted)));
      }
      spans.add(TextSpan(text: after));
    }
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text('•  '),
          Expanded(child: Text.rich(TextSpan(children: spans))),
        ],
      ),
    );
  }

  /// Kombiniert Kurz- und Langname zu „Langname (Kurzname)“ und lässt leere
  /// Teile weg. Liefert `''`, wenn beides leer ist.
  static String _formatName(String short, String long) {
    short = short.trim();
    long = long.trim();
    // Ein wörtliches „?“ zählt wie ein fehlender Wert.
    if (short == '?') short = '';
    if (long == '?') long = '';
    if (long.isNotEmpty && short.isNotEmpty && long != short) {
      return '$long ($short)';
    }
    if (long.isNotEmpty) return long;
    return short;
  }
}
