import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:veggieseasons/data/app_state.dart';
import 'package:veggieseasons/data/veggie.dart';
import 'package:veggieseasons/styles.dart';

/// Presents a series of trivia questions about a particular widget, and tracks
/// the user's score.
class TriviaView extends StatefulWidget {
  final int? id;
  final String? restorationId;

  const TriviaView({this.id, this.restorationId, super.key});

  @override
  State<TriviaView> createState() => _TriviaViewState();
}

/// Possible states of the game.
enum PlayerStatus {
  readyToAnswer,
  wasCorrect,
  wasIncorrect,
}

class _TriviaViewState extends State<TriviaView> with RestorationMixin {
  /// Current app state. This is used to fetch veggie data.
  late AppState appState;

  /// The veggie trivia about which to show.
  late Veggie veggie;

  /// Index of the current trivia question.
  RestorableInt triviaIndex = RestorableInt(0);

  /// User's score on the current veggie.
  RestorableInt score = RestorableInt(0);

  /// Trivia question currently being displayed.
  Trivia get currentTrivia => veggie.trivia[triviaIndex.value];

  /// The current state of the game.
  _RestorablePlayerStatus status =
      _RestorablePlayerStatus(PlayerStatus.readyToAnswer);

  @override
  String? get restorationId => widget.restorationId;

  @override
  void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
    registerForRestoration(triviaIndex, 'index');
    registerForRestoration(score, 'score');
    registerForRestoration(status, 'status');
  }

  // Called at init and again if any dependencies (read: InheritedWidgets) on
  // on which this object relies are changed.
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    final newAppState = Provider.of<AppState>(context);

    setState(() {
      appState = newAppState;
      veggie = appState.getVeggie(widget.id);
    });
  }

  // Called when the widget associated with this object is swapped out for a new
  // one. If the new widget has a different Veggie ID value, the state object
  // needs to do a little work to reset itself for the new Veggie.
  @override
  void didUpdateWidget(TriviaView oldWidget) {
    super.didUpdateWidget(oldWidget);

    if (oldWidget.id != widget.id) {
      setState(() {
        veggie = appState.getVeggie(widget.id);
      });

      _resetGame();
    }
  }

  @override
  void dispose() {
    triviaIndex.dispose();
    score.dispose();
    status.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (triviaIndex.value >= veggie.trivia.length) {
      return _buildFinishedView();
    } else if (status.value == PlayerStatus.readyToAnswer) {
      return _buildQuestionView();
    } else {
      return _buildResultView();
    }
  }

  void _resetGame() {
    setState(() {
      triviaIndex.value = 0;
      score.value = 0;
      status.value = PlayerStatus.readyToAnswer;
    });
  }

  void _processAnswer(int answerIndex) {
    setState(() {
      if (answerIndex == currentTrivia.correctAnswerIndex) {
        status.value = PlayerStatus.wasCorrect;
        score.value++;
      } else {
        status.value = PlayerStatus.wasIncorrect;
      }
    });
  }

  // Widget shown when the game is over. It includes the score and a button to
  // restart everything.
  Widget _buildFinishedView() {
    final themeData = CupertinoTheme.of(context);

    return Padding(
      padding: const EdgeInsets.all(32),
      child: Column(
        children: [
          Text(
            'All done!',
            style: Styles.triviaFinishedTitleText(themeData),
          ),
          const SizedBox(height: 16),
          Text('You answered', style: themeData.textTheme.textStyle),
          Row(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.center,
            textBaseline: TextBaseline.alphabetic,
            children: [
              Text(
                '${score.value}',
                style: Styles.triviaFinishedBigText(themeData),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(horizontal: 16),
                child: Text(' of ', style: themeData.textTheme.textStyle),
              ),
              Text(
                '${veggie.trivia.length}',
                style: Styles.triviaFinishedBigText(themeData),
              ),
            ],
          ),
          Text('questions correctly!', style: themeData.textTheme.textStyle),
          const SizedBox(height: 16),
          CupertinoButton(
            child: const Text('Try Again'),
            onPressed: () => _resetGame(),
          ),
        ],
      ),
    );
  }

  // Presents the current trivia's question and answer choices.
  Widget _buildQuestionView() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        children: [
          const SizedBox(height: 16),
          Text(
            currentTrivia.question,
            style: CupertinoTheme.of(context).textTheme.textStyle,
          ),
          const SizedBox(height: 32),
          for (int i = 0; i < currentTrivia.answers.length; i++)
            Padding(
              padding: const EdgeInsets.all(8),
              child: CupertinoButton(
                color: CupertinoColors.activeBlue,
                child: Text(
                  currentTrivia.answers[i],
                  textAlign: TextAlign.center,
                ),
                onPressed: () => _processAnswer(i),
              ),
            ),
        ],
      ),
    );
  }

  // Shows whether the last answer was right or wrong and prompts the user to
  // continue through the game.
  Widget _buildResultView() {
    return Padding(
      padding: const EdgeInsets.all(32),
      child: Column(
        children: [
          Text(
            status.value == PlayerStatus.wasCorrect
                ? 'That\'s right!'
                : 'Sorry, that wasn\'t the right answer.',
            style: CupertinoTheme.of(context).textTheme.textStyle,
          ),
          const SizedBox(height: 16),
          CupertinoButton(
            child: const Text('Next Question'),
            onPressed: () => setState(() {
              triviaIndex.value++;
              status.value = PlayerStatus.readyToAnswer;
            }),
          ),
        ],
      ),
    );
  }
}

class _RestorablePlayerStatus extends RestorableValue<PlayerStatus> {
  _RestorablePlayerStatus(this._defaultValue);

  final PlayerStatus _defaultValue;

  @override
  PlayerStatus createDefaultValue() {
    return _defaultValue;
  }

  @override
  PlayerStatus fromPrimitives(Object? data) {
    return PlayerStatus.values[data as int];
  }

  @override
  Object toPrimitives() {
    return value.index;
  }

  @override
  void didUpdateValue(PlayerStatus? oldValue) {
    notifyListeners();
  }
}