import 'dart:async';

import 'package:dynamic_color/dynamic_color.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart';
import 'package:grouped_list/grouped_list.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart';
import 'package:prasule/api/category.dart';
import 'package:prasule/api/entry_data.dart';
import 'package:prasule/api/wallet.dart';
import 'package:prasule/api/wallet_entry.dart';
import 'package:prasule/api/wallet_manager.dart';
import 'package:prasule/main.dart';
import 'package:prasule/network/tessdata.dart';
import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformfield.dart';
import 'package:prasule/pw/platformroute.dart';
import 'package:prasule/util/drawer.dart';
import 'package:prasule/util/sorting.dart';
import 'package:prasule/util/text_color.dart';
import 'package:prasule/util/utils.dart';
import 'package:prasule/views/create_entry.dart';
import 'package:prasule/views/settings/settings.dart';
import 'package:prasule/views/settings/tessdata_list.dart';
import 'package:prasule/views/setup.dart';

/// Main view, shows entries
class HomeView extends StatefulWidget {
  /// Main view, shows entries
  const HomeView({super.key});

  @override
  State<HomeView> createState() => _HomeViewState();
}

class _HomeViewState extends State<HomeView> {
  Wallet? selectedWallet; // current wallet
  List<Wallet> wallets = []; // all available wallets
  DateTime? prevDate;
  late String locale; // user's locale
  var _searchActive = false; // whether search field is shown
  var _filter = ""; // search filter
  final searchFocus = FocusNode();
  SortType sort = SortType.newest;
  OverlayEntry? overlayEntry;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    locale = Localizations.localeOf(context).languageCode;
    initializeDateFormatting(Localizations.localeOf(context).languageCode);
  }

  @override
  void initState() {
    super.initState();
    loadWallet();
  }

  Future<void> loadWallet() async {
    wallets = await WalletManager.listWallets();
    if (wallets.isEmpty && mounted) {
      unawaited(
        Navigator.of(context)
            .pushReplacement(platformRoute((c) => const SetupView())),
      );
      return;
    }
    selectedWallet = wallets.first;
    selectedWallet!.recurEntries();
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return PopScope(
      canPop: !_searchActive, // don't pop when we just want
      //                        to deactivate searchfield
      onPopInvoked: (b) {
        if (b) return;
        _searchActive = false;
        _filter = "";
        overlayEntry?.remove();
        setState(() {});
      },
      child: Scaffold(
        drawer: makeDrawer(context, 1),
        floatingActionButton: SpeedDial(
          shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(16)),
          ),
          icon: Icons.add,
          activeIcon: Icons.close,
          children: [
            if (kDebugMode)
              SpeedDialChild(
                child: const Icon(Icons.bug_report),
                label: AppLocalizations.of(context).createTestData,
                onTap: () {
                  // debug option to quickly fill a wallet with data
                  if (selectedWallet == null) return;
                  selectedWallet!.createTestEntries().then((_) {
                    Navigator.of(context).pushReplacement(
                      platformRoute(
                        (p0) => const HomeView(),
                      ),
                    );
                  });
                },
              ),
            SpeedDialChild(
              child: const Icon(Icons.edit),
              label: AppLocalizations.of(context).addNew,
              onTap: () async {
                final sw = await Navigator.of(context).push<Wallet>(
                  MaterialPageRoute(
                    builder: (c) => CreateSingleEntryView(
                      w: selectedWallet!,
                      locale: locale,
                    ),
                  ),
                );
                if (sw != null) {
                  selectedWallet = sw;
                }
                setState(() {});
              },
            ),
            SpeedDialChild(
              child: const Icon(Icons.camera_alt),
              label: AppLocalizations.of(context).addCamera,
              onTap: () {
                startOcr(SourceType.camera);
              },
            ),
            SpeedDialChild(
              child: const Icon(Icons.image),
              label: AppLocalizations.of(context).addGallery,
              onTap: () {
                startOcr(SourceType.photoLibrary);
              },
            ),
          ],
        ),
        appBar: AppBar(
          title: AnimatedCrossFade(
            duration: const Duration(milliseconds: 500),
            crossFadeState: _searchActive
                ? CrossFadeState.showFirst
                : CrossFadeState.showSecond,
            firstChild: PlatformField(
              inputBorder: InputBorder.none,
              focusNode: searchFocus,
              onChanged: (e) {
                _filter = e;
                setState(() {});
              },
              labelText: AppLocalizations.of(context).searchLabel,
            ),
            secondChild: DropdownButton<int>(
              value: (selectedWallet == null)
                  ? -1
                  : wallets.indexOf(
                      wallets
                          .where((w) => w.name == selectedWallet!.name)
                          .first,
                    ),
              items: [
                ...wallets.map(
                  (e) => DropdownMenuItem(
                    value: wallets.indexOf(
                      e,
                    ),
                    child: Text(e.name),
                  ),
                ),
                DropdownMenuItem(
                  value: -1,
                  child: Text(AppLocalizations.of(context).newWallet),
                ),
              ],
              onChanged: (v) async {
                if (v == null || v == -1) {
                  await Navigator.of(context).push(
                    platformRoute(
                      (c) => const SetupView(
                        newWallet: true,
                      ),
                    ),
                  );
                  wallets = await WalletManager.listWallets();
                  selectedWallet = wallets.last;
                  setState(() {});
                  return;
                }
                selectedWallet = wallets[v];
                setState(() {});
              },
            ),
          ),
          actions: _searchActive
              ? []
              : [
                  PopupMenuButton(
                    tooltip: AppLocalizations.of(context).sort,
                    icon: const Icon(Icons.sort_rounded),
                    itemBuilder: (context) => [
                      AppLocalizations.of(context).sortNewest,
                      AppLocalizations.of(context).sortOldest,
                    ]
                        .map(
                          (e) => PopupMenuItem(
                            value: e,
                            child: Text(e),
                          ),
                        )
                        .toList(),
                    onSelected: (value) {
                      if (value == AppLocalizations.of(context).sortNewest) {
                        sort = SortType.newest;
                        setState(() {});
                      } else if (value ==
                          AppLocalizations.of(context).sortOldest) {
                        sort = SortType.oldest;
                        setState(() {});
                      }
                    },
                  ),
                  PopupMenuButton(
                    itemBuilder: (context) => [
                      AppLocalizations.of(context).settings,
                      AppLocalizations.of(context).search,
                      AppLocalizations.of(context).about,
                    ]
                        .map((e) => PopupMenuItem(value: e, child: Text(e)))
                        .toList(),
                    onSelected: (value) {
                      if (value == AppLocalizations.of(context).settings) {
                        Navigator.of(context)
                            .push(
                          platformRoute(
                            (context) => const SettingsView(),
                          ),
                        )
                            .then((value) async {
                          wallets = await WalletManager.listWallets();
                          selectedWallet = await WalletManager.loadWallet(
                            selectedWallet!.name,
                          );
                          setState(() {});
                        });
                      } else if (value == AppLocalizations.of(context).about) {
                        showAbout(context);
                      } else if (value == AppLocalizations.of(context).search) {
                        _searchActive = !_searchActive;
                        if (!_searchActive) {
                          _filter = "";
                        } else {
                          overlayEntry = OverlayEntry(
                            builder: (context) => Align(
                              alignment: Alignment.bottomCenter,
                              child: SizedBox(
                                width: MediaQuery.of(context).size.width,
                                height:
                                    MediaQuery.of(context).size.height - 100,
                                child: GestureDetector(
                                  onTap: () {
                                    if (!searchFocus.hasFocus) {
                                      _searchActive = false;
                                      _filter = "";
                                      overlayEntry?.remove();
                                      setState(() {});
                                      return;
                                    }
                                    searchFocus.unfocus();
                                  },
                                ),
                              ),
                            ),
                          );
                          Overlay.of(context).insert(
                            overlayEntry!,
                          );
                        }
                        setState(() {});
                      }
                    },
                  ),
                ],
        ),
        body: Center(
          child: SizedBox(
            width: MediaQuery.of(context).size.width * 0.9,
            height: MediaQuery.of(context).size.height,
            child: (selectedWallet == null)
                ? const Column(
                    children: [
                      SizedBox(
                        width: 40,
                        height: 40,
                        child: CircularProgressIndicator(),
                      ),
                    ],
                  )
                : (selectedWallet!.entries.isEmpty)
                    ? Column(
                        children: [
                          Text(
                            AppLocalizations.of(context).noEntries,
                            style: const TextStyle(
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          Text(
                            AppLocalizations.of(context).noEntriesSub,
                          ),
                        ],
                      )
                    : Overlay(
                        initialEntries: [
                          OverlayEntry(
                            builder: (context) => Column(
                              children: [
                                Text(
                                  NumberFormat.compactCurrency(
                                    locale: locale,
                                    symbol: selectedWallet!.currency.symbol,
                                  ).format(
                                    selectedWallet!.currentBalance,
                                  ),
                                  style: const TextStyle(
                                    fontWeight: FontWeight.bold,
                                    fontSize: 22,
                                  ),
                                ),
                                const SizedBox(
                                  height: 5,
                                ),
                                if (selectedWallet!.calculateMonthStatus(
                                      DateTime.now().month,
                                      DateTime.now().year,
                                    ) ==
                                    0)
                                  Text(AppLocalizations.of(context).evenMoney)
                                else
                                  RichText(
                                    text: TextSpan(
                                      children: [
                                        TextSpan(
                                          text: AppLocalizations.of(context)
                                              .balanceStatusA,
                                          style: TextStyle(
                                            color: Theme.of(context)
                                                .colorScheme
                                                .onSurface,
                                          ),
                                        ),
                                        TextSpan(
                                          style: TextStyle(
                                            color: (selectedWallet!
                                                        .calculateMonthStatus(
                                                      DateTime.now().month,
                                                      DateTime.now().year,
                                                    ) >
                                                    0
                                                ? ((MediaQuery.of(context)
                                                                .platformBrightness ==
                                                            Brightness.dark)
                                                        ? Colors.green.shade300
                                                        : Colors.green)
                                                    .harmonizeWith(
                                                    Theme.of(context)
                                                        .colorScheme
                                                        .primary,
                                                  )
                                                : ((MediaQuery.of(context)
                                                                .platformBrightness ==
                                                            Brightness.dark)
                                                        ? Colors.red.shade300
                                                        : Colors.red)
                                                    .harmonizeWith(
                                                    Theme.of(context)
                                                        .colorScheme
                                                        .primary,
                                                  )),
                                          ),
                                          text: NumberFormat.compactCurrency(
                                            locale: locale,
                                            symbol:
                                                selectedWallet!.currency.symbol,
                                          ).format(
                                            selectedWallet!
                                                .calculateMonthStatus(
                                              DateTime.now().month,
                                              DateTime.now().year,
                                            ),
                                          ),
                                        ),
                                        TextSpan(
                                          text: AppLocalizations.of(context)
                                              .balanceStatusB,
                                          style: TextStyle(
                                            color: Theme.of(context)
                                                .colorScheme
                                                .onSurface,
                                          ),
                                        ),
                                      ],
                                    ),
                                  ),
                                const SizedBox(
                                  height: 10,
                                ),
                                Expanded(
                                  child: GroupedListView(
                                    groupHeaderBuilder: (element) => Text(
                                      DateFormat.yMMMM(locale)
                                          .format(element.date),
                                      style: TextStyle(
                                        color: Theme.of(context)
                                            .colorScheme
                                            .primary,
                                      ),
                                    ),
                                    elements: selectedWallet!.entries
                                        .where(
                                          (element) => element.data.name
                                              .toLowerCase()
                                              .contains(_filter.toLowerCase()),
                                        )
                                        .toList(),
                                    itemComparator: (a, b) =>
                                        (sort == SortType.newest)
                                            ? b.date.compareTo(a.date)
                                            : a.date.compareTo(b.date),
                                    groupBy: (e) =>
                                        DateFormat.yMMMM(locale).format(e.date),
                                    groupComparator: (a, b) =>
                                        (sort == SortType.newest)
                                            ? groupSortNewest(a, b, locale)
                                            : groupSortOldest(a, b, locale),
                                    itemBuilder: (context, element) => Slidable(
                                      endActionPane: ActionPane(
                                        extentRatio: 0.3,
                                        motion: const ScrollMotion(),
                                        children: [
                                          SlidableAction(
                                            backgroundColor: Theme.of(context)
                                                .colorScheme
                                                .error,
                                            foregroundColor: Theme.of(context)
                                                .colorScheme
                                                .onError,
                                            icon: Icons.delete,
                                            onPressed: (c) {
                                              showAdaptiveDialog<void>(
                                                context: context,
                                                builder: (cx) =>
                                                    AlertDialog.adaptive(
                                                  title: Text(
                                                    AppLocalizations.of(
                                                      context,
                                                    ).sureDialog,
                                                  ),
                                                  content: Text(
                                                    AppLocalizations.of(context)
                                                        .deleteSure,
                                                  ),
                                                  actions: [
                                                    PlatformButton(
                                                      text: AppLocalizations.of(
                                                        context,
                                                      ).yes,
                                                      onPressed: () {
                                                        selectedWallet?.entries
                                                            .removeWhere(
                                                          (e) =>
                                                              e.id ==
                                                              element.id,
                                                        );

                                                        WalletManager
                                                            .saveWallet(
                                                          selectedWallet!,
                                                        );
                                                        Navigator.of(cx).pop();
                                                        setState(() {});
                                                      },
                                                    ),
                                                    PlatformButton(
                                                      text: AppLocalizations.of(
                                                        context,
                                                      ).no,
                                                      onPressed: () {
                                                        Navigator.of(cx).pop();
                                                      },
                                                    ),
                                                  ],
                                                ),
                                              );
                                            },
                                          ),
                                        ],
                                      ),
                                      child: ListTile(
                                        onTap: () {
                                          Navigator.of(context)
                                              .push<WalletSingleEntry>(
                                            MaterialPageRoute(
                                              builder: (c) =>
                                                  CreateSingleEntryView(
                                                locale: locale,
                                                w: selectedWallet!,
                                                editEntry: element,
                                              ),
                                            ),
                                          )
                                              .then(
                                            (editedEntry) {
                                              if (editedEntry == null) {
                                                return;
                                              }
                                              selectedWallet!.entries
                                                  .remove(element);
                                              selectedWallet!.entries
                                                  .add(editedEntry);
                                              WalletManager.saveWallet(
                                                selectedWallet!,
                                              );
                                              setState(() {});
                                            },
                                          );
                                        },
                                        leading: Container(
                                          decoration: BoxDecoration(
                                            borderRadius:
                                                BorderRadius.circular(16),
                                            color: element.category.color,
                                          ),
                                          child: Padding(
                                            padding: const EdgeInsets.all(8),
                                            child: Icon(
                                              element.category.icon,
                                              color: element.category.color
                                                  .calculateTextColor(),
                                            ),
                                          ),
                                        ),
                                        title: Text(element.data.name),
                                        subtitle: RichText(
                                          text: TextSpan(
                                            children: [
                                              TextSpan(
                                                text: NumberFormat.currency(
                                                  symbol: selectedWallet!
                                                      .currency.symbol,
                                                ).format(element.data.amount),
                                                style: TextStyle(
                                                  color: (element.type ==
                                                          EntryType.income)
                                                      ? (MediaQuery.of(context)
                                                                  .platformBrightness ==
                                                              Brightness.dark)
                                                          ? Colors
                                                              .green.shade300
                                                          : Colors.green
                                                              .harmonizeWith(
                                                              Theme.of(context)
                                                                  .colorScheme
                                                                  .primary,
                                                            )
                                                      : (MediaQuery.of(context)
                                                                  .platformBrightness ==
                                                              Brightness.dark)
                                                          ? Colors.red.shade300
                                                          : Colors.red
                                                              .harmonizeWith(
                                                              Theme.of(context)
                                                                  .colorScheme
                                                                  .primary,
                                                            ),
                                                ),
                                              ),
                                              TextSpan(
                                                text:
                                                    " | ${DateFormat.MMMd(locale).format(element.date)}",
                                                style: TextStyle(
                                                  color: Theme.of(context)
                                                      .colorScheme
                                                      .surface
                                                      .calculateTextColor(),
                                                ),
                                              ),
                                            ],
                                          ),
                                        ),
                                      ),
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
          ),
        ),
      ),
    );
  }

  Future<void> startOcr(SourceType sourceType) async {
    final availableLanguages = await TessdataApi.getDownloadedData();
    if (availableLanguages.isEmpty) {
      if (!mounted) return;
      await showAdaptiveDialog<void>(
        context: context,
        builder: (c) => AlertDialog.adaptive(
          title: Text(AppLocalizations.of(context).missingOcr),
          actions: [
            PlatformButton(
              text: AppLocalizations.of(context).download,
              onPressed: () {
                Navigator.of(context)
                    .push(
                      platformRoute(
                        (c) => const TessdataListView(),
                      ),
                    )
                    .then((value) => Navigator.of(c).pop());
              },
            ),
            PlatformButton(
              text: AppLocalizations.of(context).ok,
              onPressed: () {
                Navigator.of(c).pop();
              },
            ),
          ],
        ),
      );
      return;
    }
    if (!mounted) return;
    final selectedLanguages =
        List<bool>.filled(availableLanguages.length, false);
    selectedLanguages[0] = true;

    await showAdaptiveDialog<void>(
      context: context,
      builder: (c) => StatefulBuilder(
        builder: (ctx, setDialogState) => AlertDialog.adaptive(
          actions: [
            TextButton(
              onPressed: () async {
                final filePath = await FlutterFileDialog.pickFile(
                  params: OpenFileDialogParams(
                    dialogType: OpenFileDialogType.image,
                    sourceType: sourceType,
                  ),
                );
                if (filePath == null) {
                  if (mounted) Navigator.of(context).pop();
                  return;
                }
                // get selected languages
                final selected = availableLanguages
                    .where(
                      (element) => selectedLanguages[
                          availableLanguages.indexOf(element)],
                    )
                    .join("+")
                    .replaceAll(".traineddata", "");
                logger.i(selected);
                if (!mounted) return;
                unawaited(
                  showAdaptiveDialog(
                    context: context,
                    builder: (c) => AlertDialog.adaptive(
                      title: Text(AppLocalizations.of(context).ocrLoading),
                    ),
                    barrierDismissible: false,
                  ),
                );
                final string = await FlutterTesseractOcr.extractText(
                  filePath,
                  language: selected,
                  args: {
                    "psm": "4",
                    "preserve_interword_spaces": "1",
                  },
                );
                if (!mounted) return;
                Navigator.of(context).pop();
                logger.i(string);
                if (!mounted) return;
                final lines = string.split("\n")
                  ..removeWhere((element) {
                    element.trim();
                    return element.isEmpty;
                  });
                var price = 0.0;
                final description = StringBuffer();
                for (final line in lines) {
                  // find numbered prices on each line
                  final regex = RegExp(r'\d+(?:(?:\.|,) {0,}\d{0,})+');
                  for (final match in regex.allMatches(line)) {
                    price += double.tryParse(match.group(0).toString()) ?? 0;
                  }
                  description.write("${line.replaceAll(regex, "")}\n");
                }
                if (!ctx.mounted) return;
                Navigator.of(ctx).pop();
                // show edit
                final newEntry =
                    await Navigator.of(context).push<WalletSingleEntry>(
                  platformRoute<WalletSingleEntry>(
                    (c) => CreateSingleEntryView(
                      locale: locale,
                      w: selectedWallet!,
                      editEntry: WalletSingleEntry(
                        data: EntryData(
                          name: "",
                          amount: price,
                          description: description.toString(),
                        ),
                        type: EntryType.expense,
                        date: DateTime.now(),
                        category: selectedWallet!.categories.first,
                        id: selectedWallet!.nextId,
                      ),
                    ),
                  ),
                );
                if (newEntry == null) return;
                selectedWallet!.entries.add(newEntry);
                await WalletManager.saveWallet(selectedWallet!);
                setState(() {});
              },
              child: const Text("Ok"),
            ),
            TextButton(
              onPressed: () {
                Navigator.of(c).pop();
              },
              child: const Text("Cancel"),
            ),
          ],
          title: Text(AppLocalizations.of(context).ocrSelect),
          content: Column(
            children: [
              ...List.generate(
                availableLanguages.length,
                (index) => Row(
                  children: [
                    Checkbox(
                      value: selectedLanguages[index],
                      onChanged: (value) {
                        if (value == null ||
                            (selectedLanguages
                                        .where((element) => element)
                                        .length <=
                                    1 &&
                                !value)) return;
                        selectedLanguages[index] = value;
                        setDialogState(() {});
                      },
                    ),
                    const SizedBox(
                      width: 10,
                    ),
                    Text(availableLanguages[index].split(".").first),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

/// Represents entry sorting type
enum SortType {
  /// Sort newest first
  newest,

  /// Sort oldest first
  oldest
}
