import 'dart:math';

import 'package:auto_size_text/auto_size_text.dart';
import 'package:refilc/api/providers/update_provider.dart';
import 'package:refilc_kreta_api/providers/grade_provider.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/theme/colors/colors.dart';
import 'package:refilc_kreta_api/models/grade.dart';
import 'package:refilc_kreta_api/models/subject.dart';
import 'package:refilc_kreta_api/models/group_average.dart';
import 'package:refilc_mobile_ui/common/average_display.dart';
import 'package:refilc_mobile_ui/common/empty.dart';
import 'package:refilc_mobile_ui/common/panel/panel.dart';
import 'package:refilc_mobile_ui/common/widgets/statistics_tile.dart';
import 'package:refilc_mobile_ui/common/widgets/grade/grade_subject_tile.dart';
import 'package:refilc_mobile_ui/common/trend_display.dart';
import 'package:refilc_mobile_ui/pages/grades/fail_warning.dart';
import 'package:refilc_desktop_ui/pages/grades/grades_count.dart';
import 'package:refilc_mobile_ui/pages/grades/graph.dart';
import 'package:refilc_desktop_ui/pages/grades/grade_subject_view.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:refilc/helpers/average_helper.dart';
import 'package:refilc_mobile_ui/pages/grades/average_selector.dart';
import 'grades_page.i18n.dart';

class GradesPage extends StatefulWidget {
  const GradesPage({super.key});

  @override
  GradesPageState createState() => GradesPageState();
}

class GradesPageState extends State<GradesPage> {
  late UserProvider user;
  late GradeProvider gradeProvider;
  late UpdateProvider updateProvider;
  late String firstName;
  late Widget yearlyGraph;
  List<Widget> subjectTiles = [];

  int avgDropValue = 0;

  List<Grade> getSubjectGrades(GradeSubject subject, {int days = 0}) =>
      gradeProvider.grades
          .where((e) =>
              e.subject == subject &&
              e.type == GradeType.midYear &&
              (days == 0 ||
                  e.date
                      .isBefore(DateTime.now().subtract(Duration(days: days)))))
          .toList();

  void generateTiles() {
    List<GradeSubject> subjects = gradeProvider.grades
        .map((e) => e.subject)
        .toSet()
        .toList()
      ..sort((a, b) => a.name.compareTo(b.name));
    List<Widget> tiles = [];

    Map<GradeSubject, double> subjectAvgs = {};

    tiles.addAll(subjects.map((subject) {
      List<Grade> subjectGrades = getSubjectGrades(subject);

      double avg = AverageHelper.averageEvals(subjectGrades);
      double averageBefore = 0.0;

      if (avgDropValue != 0) {
        List<Grade> gradesBefore =
            getSubjectGrades(subject, days: avgDropValue);
        averageBefore =
            avgDropValue == 0 ? 0.0 : AverageHelper.averageEvals(gradesBefore);
      }
      var nullavg = GroupAverage(average: 0.0, subject: subject, uid: "0");
      double groupAverage = gradeProvider.groupAverages
          .firstWhere((e) => e.subject == subject, orElse: () => nullavg)
          .average;

      if (avg != 0) subjectAvgs[subject] = avg;

      return GradeSubjectTile(
        subject,
        averageBefore: averageBefore,
        average: avg,
        groupAverage: avgDropValue == 0 ? groupAverage : 0.0,
        onTap: () {
          GradeSubjectView(subject, groupAverage: groupAverage)
              .push(context, root: true);
        },
      );
    }));

    if (tiles.isNotEmpty) {
      tiles.insert(0, yearlyGraph);
      tiles.insert(1, FailWarning(subjectAvgs: subjectAvgs));
      tiles.insert(
          2,
          PanelTitle(
              title: Text(avgDropValue == 0
                  ? "Subjects".i18n
                  : "Subjects_changes".i18n)));
      tiles.insert(3, const PanelHeader(padding: EdgeInsets.only(top: 12.0)));
      tiles.add(const PanelFooter(padding: EdgeInsets.only(bottom: 12.0)));
      tiles.add(const Padding(padding: EdgeInsets.only(bottom: 24.0)));
    } else {
      tiles.insert(
        0,
        Padding(
          padding: const EdgeInsets.only(top: 24.0),
          child: Empty(subtitle: "empty".i18n),
        ),
      );
    }

    double subjectAvg = subjectAvgs.isNotEmpty
        ? subjectAvgs.values.fold(0.0, (double a, double b) => a + b) /
            subjectAvgs.length
        : 0.0;
    final double classAvg = gradeProvider.groupAverages.isNotEmpty
        ? gradeProvider.groupAverages
                .map((e) => e.average)
                .fold(0.0, (double a, double b) => a + b) /
            gradeProvider.groupAverages.length
        : 0.0;

    if (subjectAvg > 0) {
      tiles.add(Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: StatisticsTile(
              fill: true,
              title: AutoSizeText(
                "subjectavg".i18n,
                textAlign: TextAlign.center,
                maxLines: 2,
                overflow: TextOverflow.ellipsis,
              ),
              value: subjectAvg,
            ),
          ),
          const SizedBox(width: 24.0),
          Expanded(
            child: StatisticsTile(
              outline: true,
              title: AutoSizeText(
                // https://discord.com/channels/1111649116020285532/1153397476578050130
                "classavg".i18n,
                textAlign: TextAlign.center,
                maxLines: 2,
                wrapWords: false,
                overflow: TextOverflow.ellipsis,
              ),
              value: classAvg,
            ),
          ),
        ],
      ));
    }

    // padding
    tiles.add(const SizedBox(height: 32.0));

    subjectTiles = List.castFrom(tiles);
  }

  @override
  Widget build(BuildContext context) {
    user = Provider.of<UserProvider>(context);
    gradeProvider = Provider.of<GradeProvider>(context);
    updateProvider = Provider.of<UpdateProvider>(context);

    List<String> nameParts = user.displayName?.split(" ") ?? ["?"];
    firstName = nameParts.length > 1 ? nameParts[1] : nameParts[0];

    final double totalClassAvg = gradeProvider.groupAverages.isEmpty
        ? 0.0
        : gradeProvider.groupAverages
                .map((e) => e.average)
                .fold(0.0, (double a, double b) => a + b) /
            gradeProvider.groupAverages.length;

    final now = gradeProvider.grades.isNotEmpty
        ? gradeProvider.grades
            .reduce((v, e) => e.date.isAfter(v.date) ? e : v)
            .date
        : DateTime.now();

    final currentStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
        .where((e) => e.type == GradeType.midYear)
        .toList());
    final prevStudentAvg = AverageHelper.averageEvals(gradeProvider.grades
        .where((e) => e.type == GradeType.midYear)
        .where((e) => e.date.isBefore(now.subtract(const Duration(days: 30))))
        .toList());

    List<Grade> graphGrades = gradeProvider.grades
        .where((e) =>
            e.type == GradeType.midYear &&
            (avgDropValue == 0 ||
                e.date.isAfter(
                    DateTime.now().subtract(Duration(days: avgDropValue)))))
        .toList();

    yearlyGraph = Padding(
      padding: const EdgeInsets.only(top: 12.0, bottom: 8.0),
      child: Panel(
        title: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            AverageSelector(
              value: avgDropValue,
              onChanged: (value) {
                setState(() {
                  avgDropValue = value!;
                });
              },
            ),
            Row(
              children: [
                // if (totalClassAvg >= 1.0) AverageDisplay(average: totalClassAvg, border: true),
                // const SizedBox(width: 4.0),
                TrendDisplay(
                    previous: prevStudentAvg, current: currentStudentAvg),
                if (gradeProvider.grades
                    .where((e) => e.type == GradeType.midYear)
                    .isNotEmpty)
                  AverageDisplay(average: currentStudentAvg),
              ],
            )
          ],
        ),
        child: Container(
          padding: const EdgeInsets.only(top: 12.0, right: 12.0),
          child: Row(
            children: [
              Expanded(
                  child: GradeGraph(graphGrades,
                      dayThreshold: 2, classAvg: totalClassAvg)),
              Padding(
                padding: const EdgeInsets.only(bottom: 24.0),
                child: GradesCount(grades: graphGrades),
              ),
            ],
          ),
        ),
      ),
    );

    generateTiles();

    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.only(top: 9.0),
        child: NestedScrollView(
          physics: const BouncingScrollPhysics(
              parent: AlwaysScrollableScrollPhysics()),
          headerSliverBuilder: (context, _) => [
            SliverAppBar(
              centerTitle: false,
              pinned: true,
              floating: false,
              snap: false,
              automaticallyImplyLeading: false,
              surfaceTintColor: Theme.of(context).scaffoldBackgroundColor,
              title: Padding(
                padding: const EdgeInsets.only(left: 8.0),
                child: Text(
                  "Grades".i18n,
                  style: TextStyle(
                      color: AppColors.of(context).text,
                      fontSize: 32.0,
                      fontWeight: FontWeight.bold),
                ),
              ),
              shadowColor: Theme.of(context).shadowColor,
            ),
          ],
          body: RefreshIndicator(
            onRefresh: () => gradeProvider.fetch(),
            color: Theme.of(context).colorScheme.secondary,
            child: ListView.builder(
              padding: EdgeInsets.zero,
              physics: const BouncingScrollPhysics(),
              itemCount: max(subjectTiles.length, 1),
              itemBuilder: (context, index) {
                if (subjectTiles.isNotEmpty) {
                  EdgeInsetsGeometry panelPadding =
                      const EdgeInsets.symmetric(horizontal: 24.0);

                  if (subjectTiles[index].runtimeType == GradeSubjectTile) {
                    return Padding(
                        padding: panelPadding,
                        child: PanelBody(
                          padding: const EdgeInsets.symmetric(horizontal: 8.0),
                          child: subjectTiles[index],
                        ));
                  } else {
                    return Padding(
                        padding: panelPadding, child: subjectTiles[index]);
                  }
                } else {
                  return Container();
                }
              },
            ),
          ),
        ),
      ),
    );
  }
}