// lib/providers/receipt_provider.dart

import 'package:flutter/material.dart';
import 'package:cashumit/providers/receipt_state.dart';
import 'package:cashumit/models/receipt_item.dart';
import 'package:cashumit/models/receipt_group.dart';
import 'package:cashumit/models/local_receipt.dart';
import 'package:cashumit/models/group_submission_result.dart';
import 'package:cashumit/services/receipt_service.dart';
import 'package:cashumit/services/local_receipt_service.dart';
import 'package:cashumit/services/account_cache_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';

class ReceiptProvider with ChangeNotifier {
  late ReceiptState _state;

  ReceiptState get state => _state;

  ReceiptProvider() {
    _state = ReceiptState();
    // Tambah 1 grup kosong default saat tampilan pertama
    _state = _state.copyWith(groups: [_newEmptyGroup()]);
  }

  /// Helper buat grup kosong baru dengan id unik
  ReceiptGroup _newEmptyGroup() {
    return ReceiptGroup(
      id: 'group_${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(1000)}',
    );
  }

  /// Initialize state and load credentials and accounts
  Future<void> initialize() async {
    await loadCredentialsAndAccounts();
  }

  /// Memuat kredensial dan akun
  Future<void> loadCredentialsAndAccounts() async {
    final credentials = await ReceiptService.loadCredentials();

    if (credentials == null) {
      _state = _state.copyWith(
        fireflyUrl: null,
        accessToken: null,
        accounts: [],
      );
      notifyListeners();
      return;
    }

    final credentialsChanged = _state.fireflyUrl != credentials['url'] ||
        _state.accessToken != credentials['token'];

    _state = _state.copyWith(
      fireflyUrl: credentials['url'],
      accessToken: credentials['token'],
    );
    // Muat akun tujuan permanen dari prefs
    await reloadDestinationFromPrefs();
    notifyListeners();

    if (credentialsChanged) {
      await loadAccounts();
      try {
        await ReceiptService.updateAccountMirror(
          baseUrl: _state.fireflyUrl!,
          accessToken: _state.accessToken!,
        );
      } catch (e) {
        print('Gagal memperbarui mirror akun: $e');
      }
    } else if (_state.accounts.isEmpty) {
      await loadAccounts();
    }
  }

  /// Muat akun tujuan permanen dari SharedPreferences.
  /// Akun tujuan dipilih sekali di Config dan dipakai semua transaksi.
  Future<void> reloadDestinationFromPrefs() async {
    final prefs = await SharedPreferences.getInstance();
    final id = prefs.getString('destination_account_id');
    final name = prefs.getString('destination_account_name');
    if (id != null && id.isNotEmpty) {
      _state = _state.copyWith(
        destinationAccountId: id,
        destinationAccountName: name,
      );
    }
  }

  /// Memuat daftar akun dengan prioritas: server > cache > fallback
  Future<void> loadAccounts() async {
    if (_state.fireflyUrl == null || _state.accessToken == null) {
      final cachedAccounts = await AccountCacheService.getCachedAccounts();
      if (cachedAccounts.isNotEmpty) {
        _state = _state.copyWith(accounts: cachedAccounts);
        notifyListeners();
      }
      return;
    }

    final allAccounts = await _getAccountsWithFallback();
    _state = _state.copyWith(accounts: allAccounts);
    notifyListeners();
  }

  Future<List<Map<String, dynamic>>> _getAccountsWithFallback() async {
    if (_state.fireflyUrl == null || _state.accessToken == null) {
      return [];
    }

    try {
      final serverAccounts = await ReceiptService.loadAccounts(
        baseUrl: _state.fireflyUrl!,
        accessToken: _state.accessToken!,
      );
      await ReceiptService.saveAccountsToMirror(serverAccounts);
      return serverAccounts;
    } catch (serverError) {
      print('Gagal memuat akun dari server: $serverError');
      try {
        final mirroredAccounts = await ReceiptService.getMirroredAccounts();
        if (mirroredAccounts.isNotEmpty) {
          return mirroredAccounts;
        }
      } catch (mirrorError) {
        print('Gagal memuat dari mirror: $mirrorError');
      }
      return [];
    }
  }

  // ============ GRUP METHODS ============

  /// Tambah grup baru
  void addGroup() {
    final id =
        'group_${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(1000)}';
    _state = _state.copyWith(
      groups: [..._state.groups, ReceiptGroup(id: id)],
    );
    notifyListeners();
  }

  /// Hapus grup berdasarkan id
  void removeGroup(String groupId) {
    _state = _state.copyWith(
      groups: _state.groups.where((g) => g.id != groupId).toList(),
    );
    notifyListeners();
  }

  /// Pilih akun sumber untuk grup tertentu
  void selectGroupSource(String groupId, String id, String name) {
    _state = _state.copyWith(
      groups: _state.groups
          .map((g) => g.id == groupId
              ? g.copyWith(sourceAccountId: id, sourceAccountName: name)
              : g)
          .toList(),
    );
    notifyListeners();
  }

  /// Tambah item ke grup tertentu
  void addItem(String groupId, ReceiptItem item) {
    _state = _state.copyWith(
      groups: _state.groups
          .map((g) => g.id == groupId
              ? g.copyWith(items: [...g.items, item])
              : g)
          .toList(),
    );
    notifyListeners();
  }

  /// Edit item di grup tertentu
  void editItem(String groupId, int index, ReceiptItem newItem) {
    _state = _state.copyWith(
      groups: _state.groups.map((g) {
        if (g.id != groupId) return g;
        if (index < 0 || index >= g.items.length) return g;
        final updatedItems = List<ReceiptItem>.from(g.items);
        updatedItems[index] = newItem;
        return g.copyWith(items: updatedItems);
      }).toList(),
    );
    notifyListeners();
  }

  /// Hapus item dari grup tertentu
  void removeItem(String groupId, int index) {
    _state = _state.copyWith(
      groups: _state.groups.map((g) {
        if (g.id != groupId) return g;
        if (index < 0 || index >= g.items.length) return g;
        final updatedItems = List<ReceiptItem>.from(g.items)..removeAt(index);
        return g.copyWith(items: updatedItems);
      }).toList(),
    );
    notifyListeners();
  }

  // ============ AKUN TUJUAN (permanen, dari Config) ============

  /// Set akun tujuan dan persist ke SharedPreferences.
  void selectDestinationAccount(String id, String name) {
    _state = _state.copyWith(
      destinationAccountId: id,
      destinationAccountName: name,
    );
    // Persist permanen
    SharedPreferences.getInstance().then((prefs) {
      prefs.setString('destination_account_id', id);
      prefs.setString('destination_account_name', name);
    });
    notifyListeners();
  }

  // ============ TIP ============

  /// Set akun sumber untuk tip (null = bukan tip)
  void setTipSourceAccount(String? id, String? name) {
    _state = _state.copyWith(
      tipSourceAccountId: id,
      tipSourceAccountName: name,
    );
    notifyListeners();
  }

  /// Hapus tip
  void clearTip() {
    _state = _state.copyWith(
      tipSourceAccountId: null,
      tipSourceAccountName: null,
    );
    notifyListeners();
  }

  // ============ TANGGAL ============

  void setTransactionDate(DateTime date) {
    _state = _state.copyWith(transactionDate: date);
    notifyListeners();
  }

  // ============ PEMBAYARAN ============

  void setPaymentAmount(double amount) {
    _state = _state.copyWith(paymentAmount: amount);
    notifyListeners();
  }

  double get changeAmount => _state.changeAmount;
  double get paymentAmount => _state.paymentAmount;
  bool get hasTip => _state.hasTip;

  String? findAccountIdByName(String name, String expectedType) {
    return ReceiptService.findAccountIdByName(
      name: name,
      expectedType: expectedType,
      accounts: _state.accounts,
    );
  }

  /// Mengirim transaksi ke Firefly III.
  /// Mengembalikan list ID transaksi (satu per grup + satu untuk tip jika ada).
  Future<List<String>> submitTransaction() async {
    final groupsWithItems =
        _state.groups.where((g) => g.items.isNotEmpty).toList();
    if (groupsWithItems.isEmpty) {
      throw Exception('Tidak ada item untuk dikirim');
    }

    if (_state.fireflyUrl == null || _state.accessToken == null) {
      throw Exception('Kredensial Firefly III tidak ditemukan');
    }

    if (_state.destinationAccountId == null) {
      throw Exception('Akun tujuan harus dipilih');
    }

    // Validasi semua grup punya sumber
    for (final g in groupsWithItems) {
      if (!g.hasSource) {
        throw Exception('Setiap grup dengan item harus memiliki akun sumber');
      }
    }

    final transactionIds = await ReceiptService.submitTransaction(
      groups: groupsWithItems,
      transactionDate: _state.transactionDate,
      destinationAccountId: _state.destinationAccountId!,
      accounts: _state.accounts,
      baseUrl: _state.fireflyUrl!,
      accessToken: _state.accessToken!,
      paymentAmount: _state.paymentAmount,
      tipSourceAccountId: _state.tipSourceAccountId,
      totalAmount: _state.total,
    );

    return transactionIds;
  }

  /// Simpan transaksi ke penyimpanan lokal
  Future<String?> saveTransactionLocally() async {
    final groupsWithItems =
        _state.groups.where((g) => g.items.isNotEmpty).toList();
    if (groupsWithItems.isEmpty) {
      throw Exception('Tidak ada item untuk disimpan');
    }

    if (_state.destinationAccountId == null) {
      throw Exception('Akun tujuan harus dipilih');
    }

    for (final g in groupsWithItems) {
      if (!g.hasSource) {
        throw Exception('Setiap grup dengan item harus memiliki akun sumber');
      }
    }

    final receiptId =
        'receipt_${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(1000)}';

    final transactionDescription =
        _generateTransactionDescription(groupsWithItems);

    final localReceipt = LocalReceipt(
      id: receiptId,
      groups: groupsWithItems,
      destinationAccountId: _state.destinationAccountId,
      destinationAccountName: _state.destinationAccountName,
      transactionDate: _state.transactionDate,
      transactionDescription: transactionDescription,
      createdAt: DateTime.now(),
      paymentAmount: _state.paymentAmount,
      tipSourceAccountId: _state.tipSourceAccountId,
      tipSourceAccountName: _state.tipSourceAccountName,
    );

    await LocalReceiptService.saveReceipt(localReceipt);

    // Reset state (grup ke 1 grup kosong; akun tujuan permanen tidak di-reset)
    _state = _state.copyWith(
      groups: [_newEmptyGroup()],
      paymentAmount: 0.0,
      tipSourceAccountId: null,
      tipSourceAccountName: null,
    );
    notifyListeners();

    return receiptId;
  }

  /// Simpan grup yang gagal dikirim sebagai nota lokal (unsubmitted),
  /// lalu reset state agar user mulai bersih. Grup sukses diabaikan.
  ///
  /// Dipakai oleh popup pengiriman transaksi setelah user tekan "Selesai".
  Future<void> saveFailedGroupsLocally(
      List<GroupSubmissionResult> results) async {
    // Ambil grup asli dari state yang cocok dengan result failed.
    final failedGroupIds = results
        .where((r) => r.isFailed && !r.isTip)
        .map((r) => r.groupId)
        .toSet();

    final hasFailedTip =
        results.any((r) => r.isTip && r.isFailed);

    if (failedGroupIds.isEmpty && !hasFailedTip) {
      // Tidak ada yang gagal; tetap reset state.
      _resetState();
      return;
    }

    final failedGroups = _state.groups
        .where((g) => failedGroupIds.contains(g.id) && g.items.isNotEmpty)
        .map((g) => ReceiptGroup(
              id: g.id,
              items: List<ReceiptItem>.from(g.items),
              sourceAccountId: g.sourceAccountId,
              sourceAccountName: g.sourceAccountName,
            ))
        .toList();

    if (failedGroups.isNotEmpty) {
      final receiptId =
          'receipt_${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(1000)}';
      final transactionDescription =
          _generateTransactionDescription(failedGroups);

      final localReceipt = LocalReceipt(
        id: receiptId,
        groups: failedGroups,
        destinationAccountId: _state.destinationAccountId,
        destinationAccountName: _state.destinationAccountName,
        transactionDate: _state.transactionDate,
        transactionDescription: transactionDescription,
        createdAt: DateTime.now(),
        paymentAmount: _state.paymentAmount,
        tipSourceAccountId:
            hasFailedTip ? _state.tipSourceAccountId : null,
        tipSourceAccountName:
            hasFailedTip ? _state.tipSourceAccountName : null,
      );

      await LocalReceiptService.saveReceipt(localReceipt);
    }

    _resetState();
  }

  /// Reset state ke kosong (grup, akun tujuan, pembayaran, tip).
  /// Catatan: destinationAccountId tidak di-reset di sini karena akun tujuan
  /// bersifat permanen (dari Config). Grup di-reset ke 1 grup kosong.
  void _resetState() {
    _state = _state.copyWith(
      groups: [_newEmptyGroup()],
      paymentAmount: 0.0,
      tipSourceAccountId: null,
      tipSourceAccountName: null,
    );
    notifyListeners();
  }

  /// Submit semua receipt lokal yang belum terkirim ke server
  Future<Map<String, dynamic>> submitAllLocalReceipts() async {
    if (_state.fireflyUrl == null || _state.accessToken == null) {
      throw Exception('Kredensial Firefly III tidak ditemukan');
    }

    final result = await LocalReceiptService.submitAllUnsubmittedReceipts(
      _state.fireflyUrl!,
      _state.accessToken!,
    );

    notifyListeners();
    return result;
  }

  String _generateTransactionDescription(List<ReceiptGroup> groups) {
    final allItems = groups.expand((g) => g.items).toList();
    if (allItems.isEmpty) {
      return 'Transaksi Struk Belanja';
    }

    final itemNames = allItems.take(5).map((item) => item.description).toList();
    if (allItems.length > 5) {
      itemNames[4] += ', dll';
    }
    return itemNames.join(', ');
  }

  /// Load receipt data to current state for editing
  void loadReceiptForEdit(LocalReceipt receipt) {
    _state = _state.copyWith(
      groups: receipt.groups
          .map((g) => ReceiptGroup(
                id: g.id,
                items: List<ReceiptItem>.from(g.items),
                sourceAccountId: g.sourceAccountId,
                sourceAccountName: g.sourceAccountName,
              ))
          .toList(),
      destinationAccountId: receipt.destinationAccountId,
      destinationAccountName: receipt.destinationAccountName,
      transactionDate: receipt.transactionDate,
      paymentAmount: receipt.paymentAmount,
      tipSourceAccountId: receipt.tipSourceAccountId,
      tipSourceAccountName: receipt.tipSourceAccountName,
    );
    notifyListeners();
  }

  /// Show payment dialog
  Future<void> showPaymentTipDialog(BuildContext context) async {
    double localPaymentAmount = _state.paymentAmount;

    await showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: const Text('Pembayaran'),
          content: StatefulBuilder(
            builder: (BuildContext context, StateSetter setState) {
              return Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Container(
                    padding: const EdgeInsets.all(8.0),
                    decoration: BoxDecoration(
                      color: Colors.grey[100],
                      borderRadius: BorderRadius.circular(4),
                    ),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text(
                          'TOTAL:',
                          style: TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        Text(
                          _state.total
                              .toStringAsFixed(0)
                              .replaceAllMapped(
                                  RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                                  (Match m) => '${m[1]},'),
                          style: const TextStyle(
                            fontSize: 14,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(height: 16),
                  TextField(
                    decoration: const InputDecoration(
                      labelText: 'Jumlah Bayar',
                      hintText: 'Masukkan jumlah uang yang dibayar',
                      labelStyle: TextStyle(fontSize: 14),
                      hintStyle: TextStyle(fontSize: 14, color: Colors.grey),
                    ),
                    keyboardType: TextInputType.number,
                    style: const TextStyle(fontSize: 14),
                    onChanged: (value) {
                      localPaymentAmount = double.tryParse(value) ?? 0.0;
                    },
                  ),
                  const SizedBox(height: 8),
                  if (localPaymentAmount > 0 &&
                      localPaymentAmount >= _state.total)
                    Container(
                      padding: const EdgeInsets.all(8.0),
                      decoration: BoxDecoration(
                        color: Colors.green[50],
                        borderRadius: BorderRadius.circular(4),
                        border: Border.all(color: Colors.green),
                      ),
                      child: Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: [
                          const Text(
                            'KEMBALI:',
                            style: TextStyle(
                              fontSize: 14,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          Text(
                            (localPaymentAmount - _state.total)
                                .toStringAsFixed(0)
                                .replaceAllMapped(
                                    RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
                                    (Match m) => '${m[1]},'),
                            style: const TextStyle(
                              fontSize: 14,
                              fontWeight: FontWeight.bold,
                              color: Colors.green,
                            ),
                          ),
                        ],
                      ),
                    ),
                  const SizedBox(height: 8),
                  const Text(
                    'Tip dapat diatur di bagian bawah struk setelah memasukkan jumlah bayar.',
                    style: TextStyle(fontSize: 11, color: Colors.grey),
                    textAlign: TextAlign.center,
                  ),
                ],
              );
            },
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('Batal'),
            ),
            TextButton(
              onPressed: () {
                setPaymentAmount(localPaymentAmount);
                // Jika pembayaran tidak mencukupi/tidak ada kembalian, hapus tip
                if (localPaymentAmount <= _state.total) {
                  clearTip();
                }
                Navigator.of(context).pop();
              },
              child: const Text('Simpan'),
            ),
          ],
        );
      },
    );
  }
}
