import 'package:flutter/material.dart';

import '../../../../api/marianumcloud/talk/close_poll/close_poll.dart';
import '../../../../api/marianumcloud/talk/get_poll/get_poll_state_response.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../api/marianumcloud/talk/vote_poll/vote_poll.dart';
import '../../../../api/marianumcloud/talk/vote_poll/vote_poll_params.dart';
import '../../../../model/account_data.dart';
import '../../../../widget/async_action_button.dart';
import '../../../../widget/confirm_dialog.dart';
import '../../../../widget/demo_restricted.dart';

/// Renders a Talk poll with interactive voting. Every displayed state comes
/// from a server response handed back via [onStateChanged]; no optimistic UI.
class PollOptionsList extends StatefulWidget {
  final GetPollStateResponseObject pollData;
  final GetRoomResponseObject room;
  final void Function(GetPollStateResponse newState) onStateChanged;

  const PollOptionsList({
    super.key,
    required this.pollData,
    required this.room,
    required this.onStateChanged,
  });

  @override
  State<PollOptionsList> createState() => _PollOptionsListState();
}

class _PollOptionsListState extends State<PollOptionsList> {
  late Set<int> _selected;
  final AsyncActionController _voteController = AsyncActionController();

  bool get _isSingleChoice => widget.pollData.maxVotes == 1;
  bool get _isInteractive =>
      !widget.pollData.isClosed && widget.room.readOnly != 1;

  @override
  void initState() {
    super.initState();
    _selected = widget.pollData.votedSelf.toSet();
  }

  @override
  void dispose() {
    _voteController.dispose();
    super.dispose();
  }

  @override
  void didUpdateWidget(covariant PollOptionsList oldWidget) {
    super.didUpdateWidget(oldWidget);
    // Resync the selection when the server state changes so it can't drift.
    if (oldWidget.pollData != widget.pollData) {
      _selected = widget.pollData.votedSelf.toSet();
    }
  }

  void _toggle(int optionId) {
    setState(() {
      if (_isSingleChoice) {
        _selected = {optionId};
        return;
      }
      if (_selected.contains(optionId)) {
        _selected.remove(optionId);
      } else {
        final max = widget.pollData.maxVotes;
        if (max > 0 && _selected.length >= max) return;
        _selected.add(optionId);
      }
    });
  }

  bool get _selectionChanged {
    final voted = widget.pollData.votedSelf.toSet();
    return !(voted.length == _selected.length && voted.containsAll(_selected));
  }

  Future<void> _submitVote() async {
    if (guardDemoAction(context)) return;
    final result = await VotePoll(
      token: widget.room.token,
      pollId: widget.pollData.id,
      params: VotePollParams(optionIds: _selected.toList()..sort()),
    ).run();
    widget.onStateChanged(result);
  }

  Future<void> _retractVote() async {
    if (guardDemoAction(context)) return;
    final result = await VotePoll(
      token: widget.room.token,
      pollId: widget.pollData.id,
      params: VotePollParams(optionIds: const []),
    ).run();
    widget.onStateChanged(result);
  }

  void _confirmClose() {
    if (guardDemoAction(context)) return;
    ConfirmDialog(
      title: 'Umfrage schließen?',
      content:
          'Danach kann niemand mehr abstimmen. '
          'Die Ergebnisse werden für alle sichtbar.',
      confirmButton: 'Schließen',
      onConfirmAsync: () async {
        final result = await ClosePoll(
          token: widget.room.token,
          pollId: widget.pollData.id,
        ).run();
        widget.onStateChanged(result);
      },
    ).asDialog(context);
  }

  @override
  Widget build(BuildContext context) {
    final poll = widget.pollData;
    final theme = Theme.of(context);
    final counts = poll.voteCounts;
    final resultsVisible = poll.resultsVisible;
    final numVoters = poll.numVoters ?? 0;

    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      mainAxisSize: MainAxisSize.min,
      children: [
        ...List.generate(poll.options.length, (optionId) {
          final option = poll.options[optionId];
          final selected = _selected.contains(optionId);
          final votes = counts['option-$optionId'] ?? 0;
          final portion = numVoters == 0 ? 0.0 : (votes / numVoters);

          return ListTile(
            isThreeLine: resultsVisible,
            dense: true,
            onTap: _isInteractive ? () => _toggle(optionId) : null,
            title: Text(option, style: theme.textTheme.bodyLarge),
            leading: _isInteractive
                ? Icon(
                    _leadingIcon(selected),
                    color: selected
                        ? theme.colorScheme.primary.withValues(alpha: 0.8)
                        : theme.colorScheme.onSurfaceVariant.withValues(
                            alpha: 0.6,
                          ),
                  )
                : null,
            subtitle: resultsVisible
                ? Row(
                    children: [
                      Expanded(
                        child: LinearProgressIndicator(
                          value: portion.clamp(0.0, 1.0),
                        ),
                      ),
                      Container(
                        margin: const EdgeInsets.only(left: 10),
                        child: Text(
                          '${votes.toInt()} · ${(portion * 100).round()}%',
                        ),
                      ),
                    ],
                  )
                : null,
          );
        }),
        if (!resultsVisible) _hiddenResultsHint(theme),
        if (poll.maxVotes > 1 && _isInteractive)
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
            child: Text(
              'Mehrfachauswahl: bis zu ${poll.maxVotes} Optionen',
              style: theme.textTheme.bodySmall,
            ),
          ),
        const Divider(height: 16),
        _metaLine(theme),
        _actionBar(poll, theme),
      ],
    );
  }

  Widget _actionBar(GetPollStateResponseObject poll, ThemeData theme) {
    final canClose = poll.canClose(
      selfId: AccountData().getUsername(),
      participantType: widget.room.participantType,
    );
    if (!_isInteractive && !canClose) return const SizedBox.shrink();
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      mainAxisSize: MainAxisSize.min,
      children: [
        AnimatedBuilder(
          animation: _voteController,
          builder: (context, _) {
            final err = _voteController.error;
            if (err == null) return const SizedBox.shrink();
            return Padding(
              padding: const EdgeInsets.only(left: 16, right: 16, bottom: 4),
              child: Text(
                err,
                textAlign: TextAlign.end,
                style: TextStyle(color: theme.colorScheme.error, fontSize: 13),
              ),
            );
          },
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
          child: Row(
            children: [
              if (canClose)
                TextButton.icon(
                  onPressed: _confirmClose,
                  icon: const Icon(Icons.lock_outline, size: 18),
                  label: const Text('Umfrage schließen'),
                ),
              const Spacer(),
              if (_isInteractive) _primaryButton(),
            ],
          ),
        ),
      ],
    );
  }

  Widget _primaryButton() {
    final voted = widget.pollData.votedSelf.isNotEmpty;

    // Retract stays a flat AsyncTextButton – its spinner uses colorScheme.primary
    // and stays visible on the sheet background, unlike the filled variant's.
    if (voted && !(_selected.isNotEmpty && _selectionChanged)) {
      return AsyncTextButton(
        controller: _voteController,
        onPressed: _retractVote,
        showInlineError: false,
        child: const Text('Zurückziehen'),
      );
    }

    final canSubmit = _selected.isNotEmpty && _selectionChanged;
    return AsyncActionButton(
      controller: _voteController,
      onPressed: canSubmit ? _submitVote : null,
      showInlineError: false,
      style: ElevatedButton.styleFrom(
        backgroundColor: Theme.of(context).colorScheme.primary,
        foregroundColor: Theme.of(context).colorScheme.onPrimary,
        elevation: 0,
      ),
      child: Text(voted ? 'Stimme ändern' : 'Abstimmen'),
    );
  }

  IconData _leadingIcon(bool selected) {
    if (_isSingleChoice) {
      return selected
          ? Icons.radio_button_checked
          : Icons.radio_button_unchecked;
    }
    return selected ? Icons.check_box : Icons.check_box_outline_blank;
  }

  Widget _hiddenResultsHint(ThemeData theme) => Padding(
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Text(
      'Die Ergebnisse werden erst nach dem Schließen der Umfrage angezeigt.',
      style: theme.textTheme.bodySmall,
    ),
  );

  Widget _metaLine(ThemeData theme) {
    final poll = widget.pollData;
    final parts = <String>[
      'Erstellt von ${poll.actorDisplayName}',
      if (poll.isClosed) 'Abgeschlossen' else 'Offen',
      if (poll.numVoters != null) '${poll.numVoters} Teilnehmer',
    ];
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
      child: Text(
        parts.join(' · '),
        style: theme.textTheme.bodySmall?.copyWith(
          color: theme.colorScheme.onSurfaceVariant,
        ),
      ),
    );
  }
}
