// ignore_for_file: use_build_context_synchronously

import 'dart:developer';
import 'dart:io';

import 'package:awesome_dialog/awesome_dialog.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:gap/gap.dart';
import 'package:go_router/go_router.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:jiffy/jiffy.dart';
import 'package:scroll_to_index/scroll_to_index.dart';

import 'package:tp5/core/basic_page.dart';
import 'package:tp5/core/core.dart';
import 'package:tp5/pdf/pdf_page.dart';
import 'package:tp5/roster/api/crewlink_api.dart';
import 'package:tp5/roster/models/duty.dart';
import 'package:tp5/roster/view/crewlist_page.dart';
import 'package:tp5/roster/widgets/w_day.dart';
import 'package:tp5/roster/widgets/w_duty.dart';
import 'package:tp5/roster/widgets/w_horizontal_month.dart';

class RosterPage extends ConsumerStatefulWidget {
  const RosterPage({required this.params, super.key});

  final RosterPageParams params;
  @override
  ConsumerState<ConsumerStatefulWidget> createState() => _RosterPageState();
}

class _RosterPageState extends ConsumerState<RosterPage> {
  late String crewlinkUser;
  late String crewlinkPass;
  Map? roster;
  List<Duty> duties = [];

  List<Jiffy> get rostermonths => duties
      .fold(
          <Jiffy>[],
          (List<Jiffy> p, Duty e) => [
                ...p,
                if (!p
                    .map((Jiffy j) => j.format(pattern: "MMMyy"))
                    .contains(e.jdate.format(pattern: "MMMyy")))
                  e.jdate
              ])
      .where((Jiffy e) => (duties
              .map((Duty d) => d.jdate.yMd)
              .contains(e.startOf(Unit.month).yMd) &&
          duties
              .map((Duty d) => d.jdate.yMd)
              .contains(e.endOf(Unit.month).yMd)))
      .toList();

  String get _rosterKey =>
      "roster_${crewlinkUser}_${widget.params.datestart}_${widget.params.dateend}";
  String get _rosterMinMaxKey => "minmax_$crewlinkUser";
  String get fileidroster => PathTo().crewlinkFile("$_rosterKey.pdf");
  String get fileidnotif => PathTo().crewlinkFile("notif_$crewlinkUser.pdf");

  final AutoScrollController _scrollCtrl = AutoScrollController();
  late Jiffy now;

  @override
  void initState() {
    // getusername & pass
    // crewlinkPass = Hive.box("profile").get("crewlink_pass");

    crewlinkUser = widget.params.crewlinkuser ??
        Hive.box("profile").get("crewlink_user") ??
        "";
    crewlinkPass = widget.params.crewlinkpass ??
        Hive.box("profile").get("crewlink_pass") ??
        "";
    Future.delayed(Duration.zero, () => _loadRoster());

    super.initState();
  }

  Future<Map?> _loadRosterOnline() async {
    if (!ref.read(crewlinkapiProvider).logged) {
      final login = await ref
          .read(crewlinkapiProvider)
          .login(username: crewlinkUser, password: crewlinkPass);
      if (login["data"]?["logged"] != true) {
        context.showError(login["error"] ?? "Unknown error");
        return null;
      }
    }

    final roster = await ref.read(crewlinkapiProvider).roster(
        start: widget.params.datestart ??
            Jiffy.now().toUtc().startOf(Unit.month).format(pattern: "ddMMMyy"),
        end: widget.params.dateend ??
            Jiffy.now().toUtc().endOf(Unit.month).format(pattern: "ddMMMyy"),
        fileid: fileidroster);

    if (roster?["data"]?["msg"] != null) {
      context.showError(roster?["data"]?["msg"] ?? "Unknown error");
    }
    if (roster["error"] == null && roster["data"] != null) {
      if (roster["msg"] != null) context.showAlert(roster["msg"]);
      Hive.box("crewlink").put(_rosterKey, roster);

      return roster;
    }

    return null;
  }

  Future<Map?> _loadRosterOffline() async {
    return Hive.box("crewlink").get(_rosterKey);
  }

  _loadRoster() async {
    try {
      ref.read(isLoadingProvider.notifier).state = true;
      final rosterOffline = await _loadRosterOffline();
      if (rosterOffline != null && mounted) {
        _convertRoster(rosterOffline);
        setState(() {});
        _scrollToDate();
      }
      final rosterOnline = await _loadRosterOnline();

      if (rosterOnline != null && mounted) {
        _convertRoster(rosterOnline);
        setState(() {});

        _scrollToDate();
      }
    } finally {
      ref.read(isLoadingProvider.notifier).state = false;
    }
  }

  _scrollToDate({Jiffy? date}) {
    final jdate = date ?? Jiffy.now().toUtc();
    bool found = false;
    int id = 0;
    for (Duty duty in duties) {
      if (duty.jdate.yMd == jdate.yMd) {
        found = true;
        break;
      }
      id++;
    }
    Future.delayed(const Duration(milliseconds: 100)).then((value) {
      if (found && mounted && _scrollCtrl.hasClients) {
        _scrollCtrl.scrollToIndex(id,
            duration: const Duration(milliseconds: 1300),
            preferPosition: AutoScrollPosition.begin);
      }
    });
  }

  _convertRoster(Map? input) {
    roster = input;
    int i = 0;
    duties.clear();
    final decoded = (input?["data"]?["decoded"]?["roster"] ?? {});
    for (var date in decoded.keys) {
      var dutylist = (decoded[date] as List);
      for (var duty in dutylist) {
        i++;
        duties.add(
            Duty(date: date, type: duty["type"], data: duty["data"], order: i));
        // print(
        //     "${(duties.last).jdate.yMd} ${(duties.last).type} ${(duties.last).start?.Hm} ${(duties.last).end?.Hm}");
      }
    }
  }

  final bottomnavstyle = ElevatedButton.styleFrom(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(5.0),
      ),
      backgroundColor:
          const Color.fromARGB(255, 0, 36, 53) //elevated btton background color
      );
  @override
  Widget build(BuildContext context) {
    now = ref.watch(clockProvider);

    return BasicPage(
      actions: [
        IconButton(
            onPressed: () => context.push("/crewlink/settings"),
            icon: const Icon(Icons.settings)),
        const Gap(10)
      ],
      bottomNavigationBar: Container(
        padding: const EdgeInsets.all(8),
        // color: Colors.black,
        decoration: BoxDecoration(
            gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [
            Colors.grey[700]!,
            Colors.black,
          ],
        )),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            ElevatedButton.icon(
              onPressed: () {
                _showMonths(context);
              },
              icon: const Icon(
                  Icons.calendar_month), //icon data for elevated button
              label: const Text("Change\nMonth"), //label text
              style: bottomnavstyle,
            ),
            const Gap(10),
            ElevatedButton.icon(
              onPressed: () {
                _showPdf(context);
              },
              icon: Image.asset(
                'assets/pdficon.png',
                width: 28,
              ), //icon data for elevated button
              label: const Text("Roster\n  PDF"), //label text
              style: bottomnavstyle,
            ),
          ],
        ),
      ),
      title: "CrewLink / Roster",
      body: ListView.builder(
        itemCount: duties.length,
        itemBuilder: (_, index) => AutoScrollTag(
            key: ValueKey(index),
            controller: _scrollCtrl,
            index: index,
            child: _getItem(_, index)),
        shrinkWrap: false,
        controller: _scrollCtrl,
      ),
    );
  }

  Widget _getItem(BuildContext ctx, int i) {
    final duty = duties.elementAt(i);
    Duty? firstitem =
        duties.firstWhereOrNull((Duty el) => el.date == duty.date);
    bool isfirstitem = false;
    if (firstitem != null &&
        firstitem.type == duty.type &&
        firstitem.data == duty.data) isfirstitem = true;
    final String lastday =
        duty.jdate.subtract(days: 1).format(pattern: "yyyy-MM-dd");

    final Duty? dutyendingtoday = (!isfirstitem)
        ? null
        : duties.firstWhereOrNull((Duty el) =>
            (el.date == lastday) &&
            (el.end != null) &&
            (el.end!.format(pattern: "yyyy-MM-dd") == duty.date));
    return Column(children: [
      if (isfirstitem) ...[
        const Gap(10),
        const Divider(),
        WDay(
          date: duty.jdate,
          highlight: now.yMd == duty.jdate.yMd,
          onTap: () => context.push("/crewlink/crewlist",
              extra: CrewlistPageParams(
                  datestart: duty.jdate.format(pattern: "ddMMMyy"))),
        ),
        if (dutyendingtoday != null)
          WDuty(duty: dutyendingtoday, date: duty.date),
      ],
      InkWell(
        highlightColor: Colors.yellow[900],
        onTap: () {
          log("Tap: ${duty.type} ${duty.data}", name: "RosterPage");
          switch (duty.type) {
            case "changed":
              AwesomeDialog(
                      context: context,
                      dialogType: DialogType.question,
                      animType: AnimType.topSlide,
                      title: 'Notification',
                      desc: 'Load and show pending notification?',
                      btnOkOnPress: () async {
                        ref.read(isLoadingProvider.notifier).state = true;

                        final res = await ref
                            .read(crewlinkapiProvider)
                            .notif(download: true, fileid: fileidnotif);
                        ref.read(isLoadingProvider.notifier).state = false;
                        if (res is Map && res["data"]?["id"] != null) {
                          _showNotif();
                        } else {
                          AwesomeDialog(
                                  context: context,
                                  dialogType: DialogType.warning,
                                  animType: AnimType.topSlide,
                                  title: 'Notification',
                                  desc: res?["error"] ??
                                      res?["data"]?["msg"] ??
                                      'No pending notification found !!!',
                                  btnOkOnPress: () async {})
                              .show();
                        }
                      },
                      btnCancelOnPress: () {},
                      btnOkText: "Load notification",
                      showCloseIcon: true)
                  .show();
              break;
            default:
          }
        },
        child: WDuty(duty: duty),
      )
    ]);
  }

  void _showNotif() {
    {
      context
          .push("/pdf",
              extra: PdfPageParams(
                  file: fileidnotif,
                  title: "Roster change",
                  bottom: ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red[900],
                    ),
                    onPressed: () async {
                      ref.read(isLoadingProvider.notifier).state = true;

                      final ack =
                          await ref.read(crewlinkapiProvider).confirmNotif();
                      // final ack = {
                      //   "error": null,
                      //   "data": {
                      //     "msg": null,
                      //     "error": null,
                      //     "notif": true
                      //   }
                      // };
                      ref.read(isLoadingProvider.notifier).state = false;
                      final ackok = (ack is Map &&
                          ack["error"] != null &&
                          ack["data"]?["error"] != null);
                      AwesomeDialog(
                          context: context,
                          dialogType:
                              ackok ? DialogType.success : DialogType.warning,
                          animType: AnimType.topSlide,
                          title: 'Notification',
                          desc:
                              // ack["data"]?["msg"] ??
                              'Notification for change was acknowledged.',
                          btnOkOnPress: () {
                            context.pop({"notif": true});
                          }).show();
                    },
                    child: const Padding(
                      padding: EdgeInsets.all(8.0),
                      child: Text(
                        'Acknowledge & Confirm changes',
                        style: TextStyle(fontSize: 20, color: Colors.yellow),
                      ),
                    ),
                  )))
          .then((res) {
        if (res is Map && res["notif"]) _loadRoster();
      });
    }
  }

  void _showPdf(xcontext) {
    if (File(fileidroster).existsSync()) {
      context.push("/pdf",
          extra: PdfPageParams(
              file: fileidroster,
              title:
                  "Roster: ${Jiffy.parse(widget.params.datestart ?? "", pattern: "ddMMMyy", isUtc: true).format(pattern: "MMMM yyyy")}"));
    } else {
      context.showError(
          "Can't find the roster for this month.\n Try to connect to internet and retrieve it from CrewLink?");
    }
  }

  void _showMonths(xcontext) async {
    ref.read(isLoadingProvider.notifier).state = true;

    final resoff = Hive.box("crewlink").get(_rosterMinMaxKey);

    final reson = await ref.read(crewlinkapiProvider).rosterMinMax();
    ref.read(isLoadingProvider.notifier).state = false;

    dynamic res;

    if (reson["error"] == null && reson?["data"] != null) {
      Hive.box("crewlink").put(_rosterMinMaxKey, reson["data"]);
      res = reson["data"];
    } else if (resoff == null) {
      context.showError(reson["error"] ?? "Unknown error");
      return;
    }
    res = res ?? resoff;

    showModalBottomSheet(
        useSafeArea: true,
        backgroundColor: Colors.blueGrey[900],
        context: context,
        builder: (BuildContext bc) {
          return HorizontalMonth(
              start: Jiffy.parse(res["mindate"],
                      pattern: "yyyy-MM-dd", isUtc: true)
                  // .add(days: 1),
                  .add(months: 1)
                  .startOf(Unit.month),
              end: Jiffy.parse(res["maxdate"],
                      pattern: "yyyy-MM-dd", isUtc: true)
                  .subtract(days: 1),
              selectedmonth: rostermonths,
              onmonthclick: (date) {
                bc.push("/crewlink/roster",
                    extra: RosterPageParams(
                        dateend:
                            date.endOf(Unit.month).format(pattern: "ddMMMyy"),
                        datestart:
                            date.startOf(Unit.month).format(pattern: "ddMMMyy"),
                        crewlinkuser: crewlinkUser,
                        crewlinkpass: crewlinkPass));
              });
        });
  }
}

class RosterPageParams {
  const RosterPageParams({
    this.dateend,
    this.datestart,
    this.crewlinkuser,
    this.crewlinkpass,
  });

  final String? dateend;
  final String? datestart;
  final String? crewlinkuser;
  final String? crewlinkpass;
}
