import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:tm_app/core/utils/device_utils.dart';
import 'package:tm_app/core/utils/logger.dart';
import 'package:tm_app/data/repositories/tenders_repository.dart';
import 'package:tm_app/data/services/model/feedback_data/feedback_data.dart';
import 'package:tm_app/data/services/model/feedback_response/feedback_response.dart';
import 'package:tm_app/data/services/model/tenders/tenders_response/tenders_response.dart';
import 'package:tm_app/views/tenders/strings/tenders_strings.dart';

import '../core/utils/result.dart';
import 'home_view_model.dart';

class TendersViewModel with ChangeNotifier {
  final TendersRepository _tendersRepository;

  TendersViewModel({
    required TendersRepository tendersRepository,
    required HomeViewModel homeViewModel,
  }) : _tendersRepository = tendersRepository;

  bool _isLoading = false;
  bool _isLoadingMore = false;
  String? _errorMessage;
  String? _feedbackErrorMessage;
  TendersResponse? _tendersResponse;
  FeedbackData? _feedbackData;
  String? _searchQuery;

  bool get isLoading => _isLoading;
  bool get isLoadingMore => _isLoadingMore;
  String? get errorMessage => _errorMessage;
  String? get feedbackErrorMessage => _feedbackErrorMessage;

  TendersResponse? get tendersResponse => _tendersResponse;
  FeedbackData? get feedbackData => _feedbackData;

  String? get searchQuery => _searchQuery;

  List<FeedbackData> feedbacks = [];

  int currentPage = 1;
  int _currentOffset = 0;
  int _totalPages = 0;
  int get totalPages => _totalPages;
  static const int _pageSize = 10;
  bool _hasMoreData = true;

  bool get hasMoreData => _hasMoreData;

  // Filter States
  DateTimeRange? selectedDateRange;
  String selectedStatus = 'All';
  String selectedSort = '';
  String sortBy = '';
  String sortOrder = '';
  final _fmt = DateFormat('yyyy/MM/dd');

  String get startDateFormatted =>
      selectedDateRange?.start != null
          ? _fmt.format(selectedDateRange!.start)
          : 'YYYY/MM/DD';

  String get endDateFormatted =>
      selectedDateRange?.end != null
          ? _fmt.format(selectedDateRange!.end)
          : 'YYYY/MM/DD';

  String get startDateUnix =>
      selectedDateRange?.start != null
          ? (selectedDateRange!.start.millisecondsSinceEpoch ~/ 1000).toString()
          : '';

  String get endDateUnix =>
      selectedDateRange?.end != null
          ? (selectedDateRange!.end.millisecondsSinceEpoch ~/ 1000).toString()
          : '';

  // --- Filter Actions ---
  void setDateRange(DateTimeRange? range) {
    selectedDateRange = range;
    notifyListeners();
  }

  void setStatus(String status) {
    // Clear data when status changes
    _clearAllData();
    selectedStatus = status;
    currentPage = 1;
    notifyListeners();
  }

  void setSort(String sort) {
    // Clear data when status changes
    _clearAllData();
    selectedSort = sort;
    if (sort == TendersStrings.lessBudget) {
      sortBy = 'estimated_value';
      sortOrder = 'asc';
    } else if (sort == TendersStrings.moreBudget) {
      sortBy = 'estimated_value';
      sortOrder = 'desc';
    } else if (sort == TendersStrings.minimumTime) {
      sortBy = 'submission_deadline';
      sortOrder = 'asc';
    } else if (sort == TendersStrings.mostOfTheTime) {
      sortBy = 'submission_deadline';
      sortOrder = 'desc';
    }
    currentPage = 1;
    notifyListeners();
  }

  void _clearAllData() {
    _tendersResponse = null;
    _errorMessage = null;
    _currentOffset = 0;
    _hasMoreData = true;
    currentPage = 1;
    sortBy = '';
    sortOrder = '';
    selectedSort = '';
    selectedStatus = '';

    notifyListeners();
  }

  void init(BuildContext context) async {
    _isLoading = true;
    notifyListeners();
    clearAll();
    await getTenders(context: context);
  }

  /// Get feedback for a specific tender
  FeedbackData? getFeedbackForTender(String tenderId) {
    try {
      return feedbacks.firstWhere((f) => f.tenderId == tenderId);
    } catch (e) {
      return null;
    }
  }

  /// Check if feedback exists for a specific tender
  bool hasFeedbackForTender(String tenderId) {
    return feedbacks.any((f) => f.tenderId == tenderId);
  }

  Future<void> getTenders({
    required BuildContext context,
    String? query,
  }) async {
    _isLoading = true;
    notifyListeners();
    if (DeviceUtils.isDesktop(context)) {
      await getTendersDesktop(query: query);
    } else {
      await getTendersMobile(query: query);
    }
  }

  Future<void> getTendersMobile({String? query}) async {
    // Clear all data before fetching new tenders

    _currentOffset = 0;
    _hasMoreData = true;
    _searchQuery = query;

    final result = await _tendersRepository.getTenders(
      limit: _pageSize,
      offset: _currentOffset,
      query: query,
      sortBy: '',
      sortOrder: '',
    );
    switch (result) {
      case Ok<TendersResponse>():
        _tendersResponse = result.value;
        // Check if we have more data
        if (_tendersResponse?.data?.tenders != null) {
          _hasMoreData = _tendersResponse!.data!.tenders!.length >= _pageSize;
        }
        if (_tendersResponse?.data?.tenders?.isNotEmpty == true) {
          // Fetch feedback and approvals for the first tender only
          getTenderFeedback(_tendersResponse!.data!.tenders!.first.id!);
        }
        if (_tendersResponse?.meta?.pages != null) {
          _totalPages = _tendersResponse!.meta!.pages!;
        } else {
          _totalPages = 1;
        }
        notifyListeners();
        break;
      case Error<TendersResponse>():
        _errorMessage = result.error.toString();
        break;
    }
    _isLoading = false;
    notifyListeners();
    _errorMessage = null;
    notifyListeners();
  }

  Future<void> getTendersDesktop({
    String? query,
    String? sortBy,
    String? sortOrder,
    String? publicationDateFrom,
    String? publicationDateTo,
  }) async {
    // Clear all data before fetching new tenders
    _isLoading = true;
    notifyListeners();
    _currentOffset = 0;
    _hasMoreData = true;
    _searchQuery = query;

    final result = await _tendersRepository.getTenders(
      limit: _pageSize,
      offset: _currentOffset,
      query: query,
      sortBy: sortBy ?? '',
      sortOrder: sortOrder ?? '',
      publicationDateFrom: publicationDateFrom ?? '',
      publicationDateTo: publicationDateTo ?? '',
    );
    switch (result) {
      case Ok<TendersResponse>():
        _tendersResponse = result.value;
        // Check if we have more data
        if (_tendersResponse?.data?.tenders != null) {
          _hasMoreData = _tendersResponse!.data!.tenders!.length >= _pageSize;
        }
        if (_tendersResponse?.data?.tenders?.isNotEmpty == true) {
          for (var tender in _tendersResponse!.data!.tenders!) {
            getTenderFeedback(tender.id!);
          }
        }
        if (_tendersResponse?.meta?.pages != null) {
          _totalPages = _tendersResponse!.meta!.pages!;
        } else {
          _totalPages = 1;
        }
        notifyListeners();
        break;
      case Error<TendersResponse>():
        _errorMessage = result.error.toString();
        break;
    }
    _isLoading = false;
    notifyListeners();
    _errorMessage = null;
    notifyListeners();
  }

  Future<void> loadMoreTenders() async {
    if (_isLoadingMore || !_hasMoreData) {
      return;
    }

    _isLoadingMore = true;
    _errorMessage = null;
    notifyListeners();

    _currentOffset += _pageSize;
    final result = await _tendersRepository.getTenders(
      limit: _pageSize,
      offset: _currentOffset,
      query: _searchQuery,
      sortBy: sortBy,
      sortOrder: sortOrder,
    );

    switch (result) {
      case Ok<TendersResponse>():
        if (result.value.data?.tenders != null &&
            result.value.data!.tenders!.isNotEmpty) {
          // Create a new modifiable list by combining existing and new tenders
          if (_tendersResponse?.data?.tenders != null) {
            final existingTenders = _tendersResponse!.data!.tenders!;
            final newTenders = result.value.data!.tenders!;
            final combinedTenders = [...existingTenders, ...newTenders];

            // Create a new TendersData with the combined list
            _tendersResponse = _tendersResponse!.copyWith(
              data: _tendersResponse!.data!.copyWith(tenders: combinedTenders),
            );
          } else {
            _tendersResponse = result.value;
          }
          // Check if we have more data
          _hasMoreData = result.value.data!.tenders!.length >= _pageSize;
        } else {
          _hasMoreData = false;
        }
        break;
      case Error<TendersResponse>():
        _errorMessage = result.error.toString();
        _currentOffset -= _pageSize; // Revert offset on error
        break;
    }

    _isLoadingMore = false;
    notifyListeners();
    _errorMessage = null;
    notifyListeners();
  }

  Future<void> getTenderFeedback(String tenderId) async {
    // _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    final result = await _tendersRepository.getTenderFeedback(
      tenderId: tenderId,
    );
    switch (result) {
      case Ok<FeedbackResponse>():
        _feedbackData = result.value.data;
        // Add or update _feedbackData in feedbacks list by tender id
        if (_feedbackData != null) {
          final index = feedbacks.indexWhere(
            (f) => f.tenderId == _feedbackData!.tenderId,
          );
          if (index != -1) {
            feedbacks[index] = _feedbackData!;
            AppLogger().info(
              'Updated feedback for tender: ${_feedbackData!.tenderId}',
            );
          } else {
            feedbacks.add(_feedbackData!);
            AppLogger().info(
              'Added new feedback for tender: ${_feedbackData!.tenderId}',
            );
          }
          AppLogger().info('Total feedbacks: ${feedbacks.length}');
        }
        break;
      case Error<FeedbackResponse>():
        _errorMessage = result.error.toString();
        break;
    }
    // _isLoading = false;
    notifyListeners();
    _errorMessage = null;
    notifyListeners();
  }

  Future<void> toogleFeedback({
    required String tenderId,
    required String feedbackType,
  }) async {
    _feedbackErrorMessage = null;

    // Get current feedback for this tender
    final existingFeedback = getFeedbackForTender(tenderId);
    final existingFeedbackIndex = feedbacks.indexWhere(
      (f) => f.tenderId == tenderId,
    );

    // Create optimistic feedback data
    FeedbackData? optimisticFeedback;

    if (existingFeedback == null) {
      // No existing feedback - create new optimistic feedback
      optimisticFeedback = FeedbackData(
        companyId: null, // Will be filled by server
        createdAt: DateTime.now().millisecondsSinceEpoch,
        customerId: null, // Will be filled by server
        feedbackType: feedbackType,
        id: null, // Will be filled by server
        tenderId: tenderId,
        tender: null,
        updatedAt: DateTime.now().millisecondsSinceEpoch,
      );
      feedbacks.add(optimisticFeedback);
    } else {
      // Existing feedback - update optimistically
      if (existingFeedback.feedbackType == feedbackType) {
        // Same feedback type - remove feedback (toggle off)
        if (existingFeedbackIndex != -1) {
          feedbacks.removeAt(existingFeedbackIndex);
        }
        optimisticFeedback = null;
      } else {
        // Different feedback type - update to new type
        optimisticFeedback = existingFeedback.copyWith(
          feedbackType: feedbackType,
          updatedAt: DateTime.now().millisecondsSinceEpoch,
        );
        if (existingFeedbackIndex != -1) {
          feedbacks[existingFeedbackIndex] = optimisticFeedback;
        }
      }
    }

    // Notify listeners immediately for optimistic update
    notifyListeners();

    // Call API
    final result = await _tendersRepository.toggleFeedback(
      tenderId: tenderId,
      feedbackType: feedbackType,
    );

    switch (result) {
      case Ok<FeedbackResponse>():
        _feedbackData = result.value.data;
        if (_feedbackData != null) {
          // Update with complete data from server
          final index = feedbacks.indexWhere(
            (f) => f.tender == _feedbackData!.tender,
          );
          if (index != -1) {
            feedbacks[index] = _feedbackData!;
          } else {
            feedbacks.add(_feedbackData!);
          }
        } else {
          // Server returned null data - remove feedback
          feedbacks.removeWhere((f) => f.tenderId == tenderId);
        }
        break;
      case Error<FeedbackResponse>():
        // Revert optimistic update on error
        if (existingFeedback == null) {
          // Remove the optimistic feedback we added
          feedbacks.removeWhere((f) => f.tenderId == tenderId);
        } else {
          // Restore the original feedback - find current index and update, or add if not found
          final currentIndex = feedbacks.indexWhere(
            (f) => f.tenderId == tenderId,
          );
          if (currentIndex != -1) {
            feedbacks[currentIndex] = existingFeedback;
          } else {
            feedbacks.add(existingFeedback);
          }
        }
        _feedbackErrorMessage = result.error.toString();
        break;
    }

    notifyListeners();
    _feedbackErrorMessage = null;
    notifyListeners();
  }

  /// Clear all feedbacks
  void clearFeedbacks() {
    feedbacks.clear();
    _feedbackData = null;
    _feedbackErrorMessage = null;
    notifyListeners();
  }

  /// Clear all tenders
  void clearTenders() {
    _tendersResponse = null;
    _errorMessage = null;
    _currentOffset = 0;
    _hasMoreData = true;
    currentPage = 1;
    notifyListeners();
  }

  Future<void> callWithSortAndStatus() async {
    await getTendersDesktop(
      sortBy: sortBy,
      sortOrder: sortOrder,
      publicationDateFrom: startDateUnix,
      publicationDateTo: endDateUnix,
    );
  }

  /// Search tenders with query
  Future<void> searchTenders(String query) async {
    await getTendersDesktop(
      query: query.trim(),
      sortBy: sortBy,
      sortOrder: sortOrder,
      publicationDateFrom: startDateUnix,
      publicationDateTo: endDateUnix,
    );
  }

  /// Clear all data (tenders, feedbacks, and approvals)
  void clearAll() {
    clearTenders();
    clearFeedbacks();
    _searchQuery = null;
    selectedSort = '';
    selectedStatus = '';
    selectedDateRange = null;
    sortBy = '';
    sortOrder = '';
    notifyListeners();
  }

  void jumpToPage(int page) async {
    final meta = tendersResponse?.meta;
    if (meta?.pages == null) {
      return;
    }
    if (page < 1 || page > meta!.pages!) {
      return;
    }

    currentPage = page;
    _currentOffset = (page - 1) * _pageSize;

    final result = await _tendersRepository.getTenders(
      limit: _pageSize,
      offset: _currentOffset,
      query: _searchQuery,
      sortBy: sortBy,
      sortOrder: sortOrder,
    );

    switch (result) {
      case Ok<TendersResponse>():
        _tendersResponse = result.value;
        break;
      case Error<TendersResponse>():
        _errorMessage = result.error.toString();
        break;
    }

    notifyListeners();
  }
}
