import 'package:flutter/material.dart';

import '../../../api/errors/error_mapper.dart';
import '../../../api/marianumconnect/queries/absence/absence_classes.dart';
import '../../../api/marianumconnect/queries/absence/absence_prefill.dart';
import '../../../api/marianumconnect/queries/absence/absence_prefill_response.dart';
import '../../../api/marianumconnect/queries/absence/absence_submit.dart';
import '../../../extensions/date_time.dart';
import '../../../widget/app_progress_indicator.dart';
import '../../../widget/async_action_button.dart';
import '../../../widget/demo_restricted.dart';
import '../../../widget/focus_behaviour.dart';
import '../../../widget/placeholder_view.dart';

/// Mobile mirror of the public absence-report form: submit-only (no history —
/// that lives on the web). Identity/class/phone are prefilled from the backend
/// but stay editable; the class list matches the submit validation source.
/// User-facing texts mirror the public web form (`AbsencePublicForm.svelte`).
class AbsenceReportView extends StatefulWidget {
  const AbsenceReportView({super.key});

  @override
  State<AbsenceReportView> createState() => _AbsenceReportViewState();
}

class _AbsenceReportViewState extends State<AbsenceReportView> {
  static const String _required = 'Dieses Feld ist erforderlich.';

  final TextEditingController _firstName = TextEditingController();
  final TextEditingController _lastName = TextEditingController();
  final TextEditingController _phone = TextEditingController();
  final TextEditingController _note = TextEditingController();
  final AsyncActionController _submitController = AsyncActionController();

  late Future<void> _init;
  List<String> _classes = const [];
  String? _selectedClass;
  late DateTime _absentFrom;
  late DateTime _absentUntil;
  bool _submitted = false;
  bool _done = false;

  @override
  void initState() {
    super.initState();
    final today = DateUtils.dateOnly(DateTime.now());
    _absentFrom = today;
    _absentUntil = today;
    for (final c in [_firstName, _lastName, _phone, _note]) {
      c.addListener(_onFieldChanged);
    }
    _init = _load();
  }

  @override
  void dispose() {
    for (final c in [_firstName, _lastName, _phone, _note]) {
      c.dispose();
    }
    _submitController.dispose();
    super.dispose();
  }

  // Once a submit surfaced errors, re-render on every keystroke so the inline
  // field errors clear as soon as the offending field is filled.
  void _onFieldChanged() {
    if (_submitted) setState(() {});
  }

  Future<void> _load() async {
    // Both GETs are independent — fire them together. Prefill is best-effort
    // (mapped to null on failure), so a classes error still propagates while a
    // prefill failure never surfaces as an unhandled async error.
    final classesFuture = AbsenceClasses().run();
    final prefillFuture = AbsencePrefill().run().then<AbsencePrefillResponse?>(
      (p) => p,
      onError: (_) => null,
    );
    final classes = await classesFuture;
    final prefill = await prefillFuture;
    if (prefill != null) _applyPrefill(prefill, classes);
    _classes = classes;
  }

  void _applyPrefill(AbsencePrefillResponse p, List<String> classes) {
    if (_firstName.text.isEmpty) _firstName.text = p.firstName;
    if (_lastName.text.isEmpty) _lastName.text = p.lastName;
    if (_phone.text.isEmpty) _phone.text = p.phone;
    if (_selectedClass == null && classes.contains(p.className)) {
      _selectedClass = p.className;
    }
  }

  bool get _startInPast =>
      _absentFrom.isBefore(DateUtils.dateOnly(DateTime.now()));

  bool get _endBeforeStart => _absentUntil.isBefore(_absentFrom);

  Future<void> _pickFrom() async {
    final today = DateUtils.dateOnly(DateTime.now());
    final picked = await showDatePicker(
      context: context,
      initialDate: _absentFrom.isBefore(today) ? today : _absentFrom,
      firstDate: today,
      lastDate: DateTime(today.year + 1, today.month, today.day),
    );
    if (picked == null) return;
    setState(() {
      _absentFrom = picked;
      if (_endBeforeStart) _absentUntil = _absentFrom;
    });
  }

  Future<void> _pickUntil() async {
    final picked = await showDatePicker(
      context: context,
      initialDate: _endBeforeStart ? _absentFrom : _absentUntil,
      firstDate: _absentFrom,
      lastDate: DateTime(
        _absentFrom.year + 1,
        _absentFrom.month,
        _absentFrom.day,
      ),
    );
    if (picked == null) return;
    setState(() => _absentUntil = picked);
  }

  Future<void> _submit() async {
    if (guardDemoAction(context)) return;
    final valid =
        _firstName.text.trim().isNotEmpty &&
        _lastName.text.trim().isNotEmpty &&
        _selectedClass != null &&
        _phone.text.trim().isNotEmpty &&
        _note.text.trim().isNotEmpty &&
        !_startInPast &&
        !_endBeforeStart;
    if (!valid) {
      setState(() => _submitted = true);
      return;
    }
    await AbsenceSubmit().run(
      firstName: _firstName.text.trim(),
      lastName: _lastName.text.trim(),
      className: _selectedClass!,
      absentFrom: _absentFrom,
      absentUntil: _absentUntil,
      phone: _phone.text.trim(),
      note: _note.text.trim(),
    );
    if (!mounted) return;
    // Replace the whole form with a terminal success screen. There is
    // deliberately no way back to the form here — to file another report the
    // user leaves the module and re-enters (which builds a fresh form).
    setState(() => _done = true);
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(title: const Text('Abwesenheitsmeldung')),
    body: _done ? const _SubmittedView() : _buildBody(context),
  );

  Widget _buildBody(BuildContext context) => FutureBuilder<void>(
    future: _init,
    builder: (context, snapshot) {
      if (snapshot.connectionState != ConnectionState.done) {
        return const Center(child: AppProgressIndicator.large());
      }
      if (snapshot.hasError) {
        return PlaceholderView(
          icon: Icons.error_outline,
          text: errorToUserMessage(snapshot.error),
          button: ElevatedButton.icon(
            onPressed: () {
              final reload = _load();
              setState(() {
                _init = reload;
              });
            },
            icon: const Icon(Icons.refresh),
            label: const Text('Erneut versuchen'),
          ),
        );
      }
      return _buildForm(context);
    },
  );

  Widget _buildForm(BuildContext context) {
    final theme = Theme.of(context);
    return SingleChildScrollView(
      padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text(
            'Bitte tragen Sie hier die voraussichtliche Abwesenheit ein. '
            'Bitte denken Sie auch an die schriftliche Entschuldigung.',
            style: theme.textTheme.bodyMedium?.copyWith(
              color: theme.colorScheme.onSurfaceVariant,
            ),
          ),
          const SizedBox(height: 20),
          TextField(
            controller: _firstName,
            textCapitalization: TextCapitalization.words,
            decoration: _decoration(
              'Vorname',
              error: _requiredError(_firstName),
            ),
            onTapOutside: (_) => FocusBehaviour.textFieldTapOutside(context),
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _lastName,
            textCapitalization: TextCapitalization.words,
            decoration: _decoration(
              'Nachname',
              error: _requiredError(_lastName),
            ),
            onTapOutside: (_) => FocusBehaviour.textFieldTapOutside(context),
          ),
          const SizedBox(height: 16),
          DropdownButtonFormField<String>(
            initialValue: _selectedClass,
            isExpanded: true,
            hint: const Text('— bitte wählen —'),
            decoration: _decoration(
              'Klasse',
              error: _submitted && _selectedClass == null ? _required : null,
            ),
            items: _classes
                .map((c) => DropdownMenuItem(value: c, child: Text(c)))
                .toList(),
            onChanged: (value) => setState(() => _selectedClass = value),
          ),
          const SizedBox(height: 16),
          _DateField(
            label: 'Fehlt ab',
            value: _absentFrom.formatDate(),
            error: _submitted && _startInPast
                ? 'Das Startdatum darf nicht in der Vergangenheit liegen.'
                : null,
            onTap: _pickFrom,
          ),
          const SizedBox(height: 16),
          _DateField(
            label: 'bis',
            value: _absentUntil.formatDate(),
            error: _submitted && _endBeforeStart
                ? 'Das Enddatum darf nicht vor dem Startdatum liegen.'
                : null,
            onTap: _pickUntil,
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _phone,
            keyboardType: TextInputType.phone,
            decoration: _decoration(
              'Telefonnummer für Rückfragen',
              error: _requiredError(_phone),
            ),
            onTapOutside: (_) => FocusBehaviour.textFieldTapOutside(context),
          ),
          const SizedBox(height: 16),
          TextField(
            controller: _note,
            minLines: 3,
            maxLines: 6,
            textCapitalization: TextCapitalization.sentences,
            decoration: _decoration(
              'Bemerkung / Grund',
              error: _requiredError(_note),
            ),
            onTapOutside: (_) => FocusBehaviour.textFieldTapOutside(context),
          ),
          const SizedBox(height: 24),
          AsyncActionButton(
            controller: _submitController,
            onPressed: _submit,
            child: const Text('Abwesenheit melden'),
          ),
        ],
      ),
    );
  }

  String? _requiredError(TextEditingController controller) =>
      _submitted && controller.text.trim().isEmpty ? _required : null;

  InputDecoration _decoration(String label, {String? error, String? hint}) =>
      InputDecoration(
        border: const OutlineInputBorder(),
        labelText: label,
        hintText: hint,
        errorText: error,
      );
}

/// Terminal success screen shown in place of the form after a report was
/// submitted. Mirrors the public web form's done page and intentionally offers
/// no path back to the form — filing another report means re-entering the
/// module.
class _SubmittedView extends StatelessWidget {
  const _SubmittedView();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Center(
      child: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Padding(
              padding: const EdgeInsets.only(bottom: 20),
              child: Icon(
                Icons.check_circle_outline,
                size: 60,
                color: theme.colorScheme.primary,
              ),
            ),
            Text(
              'Ihre Abwesenheit wurde erfolgreich übermittelt!',
              textAlign: TextAlign.center,
              style: theme.textTheme.titleLarge,
            ),
            const SizedBox(height: 12),
            Text(
              'Vielen Dank und bei Krankheit gute Besserung!',
              textAlign: TextAlign.center,
              style: theme.textTheme.bodyLarge?.copyWith(
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 16),
            Text(
              'Bei Fragen wenden Sie sich bitte an unser Sekretariat. '
              'Tel: 0661-969120',
              textAlign: TextAlign.center,
              style: theme.textTheme.bodyMedium?.copyWith(
                color: theme.colorScheme.onSurfaceVariant,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class _DateField extends StatelessWidget {
  final String label;
  final String value;
  final String? error;
  final VoidCallback onTap;

  const _DateField({
    required this.label,
    required this.value,
    required this.error,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) => InkWell(
    onTap: onTap,
    child: InputDecorator(
      decoration: InputDecoration(
        border: const OutlineInputBorder(),
        labelText: label,
        errorText: error,
      ),
      child: Row(
        children: [
          Icon(
            Icons.date_range_outlined,
            color: Theme.of(context).colorScheme.onSurfaceVariant,
          ),
          const SizedBox(width: 12),
          Text(value, style: Theme.of(context).textTheme.bodyLarge),
        ],
      ),
    ),
  );
}
