// 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/csv/data.dart';
import 'package:tp5/fltinfo/view/dutyinfo_page.dart';
import 'package:tp5/fltinfo/view/fltinfo_page.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("settings").get("crewlink_pass");

    crewlinkUser = widget.params.crewlinkuser ??
        Hive.box("settings").get("crewlink_user") ??
        "";
    crewlinkPass = widget.params.crewlinkpass ??
        Hive.box("settings").get("crewlink_pass") ??
        "";
    Future.delayed(Duration(milliseconds: 700), () => _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 {
      if (mounted) {
        ref.read(isLoadingProvider.notifier).state = false;
      }
    }
  }

  String get tlc => crewlinkUser;
  _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}");
      }
    }
    _calculHours();
  }

  Duration _fltHrs = Duration.zero;
  num _nbIso = 0;
  Duration _credit = Duration.zero;

  _calculHours() {
    _fltHrs = duties
        .where((e) => e.jdate.yM == duties.firstOrNull?.jdate.yM)
        .fold(Duration.zero, (t, e) {
      switch (e.type) {
        case "flight":
          return t + DTInterval(e.start!, e.end!).duration;
        default:
          return t;
      }
    });

    _nbIso = duties.fold(
        0,
        (t, e) =>
            t +
            ((((duties
                            .firstWhereOrNull((f) =>
                                (f.jdate.yMEd == e.jdate.yMEd &&
                                    f.type == "credit"))
                            ?.data["credit"]) ??
                        "0:00") ==
                    "0:00")
                ? 0
                : 1));

    _credit = duties
        .where((e) => e.jdate.yM == duties.firstOrNull?.jdate.yM)
        .fold(Duration.zero, (t, e) {
      switch (e.type) {
        case "flight":
          return t + DTInterval(e.start!, e.end!).duration;
        case "dhflight":
          return t + DTInterval(e.start!, e.end!).duration;
        case "dhlimo":
          return t + DTInterval(e.start!, e.end!).duration;
        case "ground":
          if (e.data["label"].startsWith("SBY")) {
            return t + const Duration(hours: 6, minutes: 10);
          }
          if (["CA", "CM", "OFF", "PP", "FR"].contains(e.data["label"])) {
            return t;
          }
          return t;
        default:
          return t;
      }
    }).add(_nbIso >= 3
            ? Duration.zero
            : (const Duration(hours: 2, minutes: 10)
                .multiply(_nbIso.toDouble())));
  }

  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,
            ),
            const Gap(10),
            Column(mainAxisSize: MainAxisSize.min, children: [
              Text(
                "Flt: ${_fltHrs.tohhmm}",
                style:
                    TextStyle(color: Colors.white, fontWeight: FontWeight.w700),
              ),
              Text(
                "Credit: [${_credit.tohhmm}]",
                style: TextStyle(color: Colors.blue),
              ),
            ])
          ],
        ),
      ),
      title: "CrewLink / Roster",
      body: ListView.builder(
        itemCount: duties.length,
        itemBuilder: (contextx, index) => AutoScrollTag(
            key: ValueKey(index),
            controller: _scrollCtrl,
            index: index,
            child: _getItem(contextx, 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.go("/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.start?.Hm} ${duty.type} ${duty.data}",name: "RosterPage");
          if (duty.type == "changed") {
            AlertDialog(
              title: const Text('Notification'),
              content: const Text('Load and show pending notification?'),
              actions: [
                TextButton(
                    child: const Text("Load Notification",
                        style: TextStyle(color: Colors.red, fontSize: 16)),
                    onPressed: () async {
                      ref.read(isLoadingProvider.notifier).state = true;

                      final res = await ref
                          .read(crewlinkapiProvider)
                          .notif(download: true, fileid: fileidnotif);
                      if (mounted) {
                        ref.read(isLoadingProvider.notifier).state = false;
                      }
                      if (res is Map && res["data"]?["id"] != null) {
                        _showNotif();
                      } else {
                        AlertDialog(
                            title: const Text('Notification'),
                            content: Text(res?["error"] ??
                                res?["data"]?["msg"] ??
                                'No pending notification found !!!'),
                            actions: [
                              TextButton(
                                  child: const Text("OK"),
                                  onPressed: () => context.pop())
                            ]).show(context);
                      }
                    }),
                TextButton(
                    child: const Text(
                      "Discard",
                      style: TextStyle(color: Colors.green),
                    ),
                    onPressed: () => context.pop())
              ],
            ).show(context);
          } else if (duty.type == "flight") {
            context.push("/fltinfo",
                extra: FltinfoParams(
                    al: duty.data["al"],
                    fnum: duty.data["fnum"],
                    dep: duty.data["dep"],
                    des: duty.data["des"],
                    jdep: duty.start,
                    jdes: duty.end));
          } else if (duty.type == "dhflight") {
            context.push("/fltinfo",
                extra: FltinfoParams(
                    al: duty.data["al"],
                    fnum: duty.data["fnum"],
                    dep: duty.data["dep"],
                    des: duty.data["des"],
                    jdep: duty.start,
                    jdes: duty.end));
          } else if (duty.type == "dhlimo") {
            final pseudoleg = Pnleg(
                dep: duty.data["dep"],
                arr: duty.data["des"],
                depdate: duty.start?.format(pattern: "dd/MM/yyyy"),
                deptime: duty.start?.format(pattern: "HHmm"),
                arrdate: duty.end?.format(pattern: "dd/MM/yyyy"),
                arrtime: duty.end?.format(pattern: "HHmm"),
                //label: duty.data["label"],
                type: "G");
            context.push("/dutyinfo",
                extra: DutyinfoParams(
                    dutytype: pseudoleg.dutytype,
                    jdep: duty.start,
                    jdes: duty.end,
                    dep: duty.data["dep"],
                    des: duty.data["des"]));
          } else if (duty.type == "ground") {
            final pseudoleg = Pnleg(
              tlc: tlc,
              dep: duty.data["dep"],
              arr: duty.data["des"],
              depdate: duty.start?.format(pattern: "dd/MM/yyyy"),
              deptime: duty.start?.format(pattern: "HHmm"),
              arrdate: duty.end?.format(pattern: "dd/MM/yyyy"),
              arrtime: duty.end?.format(pattern: "HHmm"),
              label: duty.data["label"],
              type: "A",
            );
            if (pseudoleg.dutytype == "standby") {
              context.push("/dutyinfo",
                  extra: DutyinfoParams(
                    dutytype: pseudoleg.dutytype,
                    tlc: pseudoleg.tlc,
                    //label: pseudoleg.label,
                    jdep: pseudoleg.jdep,
                    jdes: pseudoleg.jarr,
                    //start: duty.start,
                    //end: duty.end,
                    dep: pseudoleg.dep,
                    des: pseudoleg.arr,
                    sameday: true,
                  ));
            } else {
              context.push("/dutyinfo",
                  extra: DutyinfoParams(
                    // dutytype: pseudoleg.dutytype,
                    label: pseudoleg.label,
                    jdep: duty.start,
                    jdes: duty.end,
                    // start: duty.start,
                    // end: duty.end,
                    dep: duty.data["dep"],
                    des: duty.data["des"],
                    // sameday: true,
                  ));
            }
          }
        },
        child: WDuty(duty: duty),
      )
    ]);
  }

  void _showNotif() {
    {
      context.pop();
      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
                      //   }
                      // };
                      if (mounted) {
                        ref.read(isLoadingProvider.notifier).state = false;
                      }
                      final ackok = (ack is Map && ack["error"] == null
                          //&&
                          //ack["data"]?["error"] != null
                          );
                      if (ackok) {
                        AlertDialog(
                            title: const Text('Notification'),
                            content:
                                // ack["data"]?["msg"] ??
                                const Text(
                                    'Notification for change was acknowledged.'),
                            actions: [
                              TextButton(
                                  child: const Text("OK"),
                                  onPressed: () {
                                    context.pop({"notif": true});
                                  })
                            ]).show(context);
                      } else {
                        AlertDialog(
                            title: const Text('Notification'),
                            content:
                                // ack["data"]?["msg"] ??
                                const Text(
                                    'A Problem has occured. Notification not confirmed.'),
                            actions: [
                              TextButton(
                                  child: const Text("OK"),
                                  onPressed: () {
                                    context.pop({"notif": false});
                                  })
                            ]).show(context);
                      }
                    },
                    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();

        //! check if the roster is still the same otherwise call _loadRoster
        if (res is Map && res["notif"])
          context.pushReplacement("/crewlink/roster");
      });
    }
  }

  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();
    if (mounted) {
      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;
}
