import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/storage/draft_storage.dart';
import 'expense_model.dart';
import 'expense_api.dart';

const expenseDraftKey = 'expense';

class ExpenseCreateState {
  final ExpenseModel expense;
  final bool isSubmitting;

  const ExpenseCreateState({required this.expense, this.isSubmitting = false});

  ExpenseCreateState copyWith({ExpenseModel? expense, bool? isSubmitting}) {
    return ExpenseCreateState(
      expense: expense ?? this.expense,
      isSubmitting: isSubmitting ?? this.isSubmitting,
    );
  }
}

class ExpenseCreateController extends StateNotifier<ExpenseCreateState> {
  final ExpenseApi _api;

  ExpenseCreateController(this._api, {ExpenseModel? initial})
    : super(
        ExpenseCreateState(
          expense:
              initial ??
              ExpenseModel(
                id: '',
                expenseNo: '',
                createTime: DateTime.now(),
                updateTime: DateTime.now(),
              ),
        ),
      );

  void updateCurrencyCode(String code) {
    state = state.copyWith(expense: state.expense.copyWith(currencyCode: code));
  }

  void updateApprovedAmount(double amount) {
    state = state.copyWith(expense: state.expense.copyWith(approvedAmount: amount));
  }

  void updateDetailApprovedAmount(int index, double amount) {
    final details = [...state.expense.details];
    details[index] = details[index].copyWith(approvedAmount: amount);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void updateDetailCustomerVendor(int index, String name) {
    final details = [...state.expense.details];
    details[index] = details[index].copyWith(customerVendorName: name);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void updateDetailProjectId(int index, String id) {
    final details = [...state.expense.details];
    details[index] = details[index].copyWith(projectId: id);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void updateDetailAcctSubjectId(int index, String id) {
    final details = [...state.expense.details];
    details[index] = details[index].copyWith(acctSubjectId: id);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void updateDetailOffsetAmount(int index, double amount) {
    final details = [...state.expense.details];
    details[index] = details[index].copyWith(offsetAmount: amount);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void restoreFromDraft(ExpenseModel draft, ExpenseApi api) {
    state = ExpenseCreateState(expense: draft);
  }

  void reset() {
    state = ExpenseCreateState(
      expense: ExpenseModel(
        id: '',
        expenseNo: '',
        createTime: DateTime.now(),
        updateTime: DateTime.now(),
      ),
    );
  }

  void updatePurpose(String purpose) {
    state = state.copyWith(expense: state.expense.copyWith(purpose: purpose));
  }

  ExpenseCreateState get currentState => state;

  void updateAttachments(List<String> paths) {
    state = state.copyWith(expense: state.expense.copyWith(attachments: paths));
  }

  void updatePaymentMethod(String method) {
    state = state.copyWith(expense: state.expense.copyWith(paymentMethod: method));
  }

  void updateRemark(String remark) {
    state = state.copyWith(expense: state.expense.copyWith(remark: remark));
  }

  void setGenerateVoucher(bool value) {
    state = state.copyWith(expense: state.expense.copyWith(isGenerateVoucher: value));
  }

  void updateDept(String deptId, String deptName) {
    state = state.copyWith(expense: state.expense.copyWith(deptId: deptId, deptName: deptName));
  }

  void addDetail(ExpenseDetailModel detail) {
    final details = [...state.expense.details, detail];
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void removeDetail(int index) {
    final details = [...state.expense.details]..removeAt(index);
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void removeDetailsByAeNo(String aeNo) {
    final details = state.expense.details.where((d) => d.aeNo != aeNo).toList();
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void updateDetail(int index, ExpenseDetailModel detail) {
    final details = [...state.expense.details];
    details[index] = detail;
    state = state.copyWith(expense: state.expense.copyWith(details: details));
  }

  void recalculateAmount() {
    var totalAmount = 0.0;
    var approvedAmount = 0.0;
    for (final d in state.expense.details) {
      totalAmount += d.totalAmount;
      approvedAmount += d.approvedAmount;
    }
    state = state.copyWith(
      expense: state.expense.copyWith(
        totalAmount: totalAmount,
        approvedAmount: approvedAmount,
      ),
    );
  }

  Future<bool> submit() async {
    state = state.copyWith(isSubmitting: true);
    try {
      await _api.submit(state.expense.copyWith(status: 'pending').toJson());
      await DraftStorage.delete(expenseDraftKey);
      return true;
    } catch (_) {
      return false;
    } finally {
      state = state.copyWith(isSubmitting: false);
    }
  }

  Future<bool> saveDraft() async {
    state = state.copyWith(isSubmitting: true);
    try {
      await DraftStorage.save(expenseDraftKey, state.expense.toJson());
      return true;
    } catch (_) {
      return false;
    } finally {
      state = state.copyWith(isSubmitting: false);
    }
  }

  static Future<ExpenseModel?> loadDraft() async {
    final data = await DraftStorage.load(expenseDraftKey);
    if (data == null) return null;
    try {
      return ExpenseModel.fromJson(data);
    } catch (_) {
      return null;
    }
  }

  static Future<bool> hasDraft() => DraftStorage.has(expenseDraftKey);

  static Future<void> deleteDraft() => DraftStorage.delete(expenseDraftKey);
}

final expenseCreateProvider = StateNotifierProvider.autoDispose
    .family<ExpenseCreateController, ExpenseCreateState, String?>((ref, editId) {
      final api = ref.watch(expenseApiProvider);
      return ExpenseCreateController(api);
    });
