import 'dart:async';
import 'package:flutter/material.dart';
import 'models/job.dart';
import 'services/database_service.dart';
import 'services/dart_mq.dart';
import 'l10n/app_localizations.dart';

/// Global notifier for language changes
final ValueNotifier<Locale> localeNotifier = ValueNotifier<Locale>(const Locale('de'));

class AppState {
  static final AppState _instance = AppState._internal();
  factory AppState() => _instance;
  AppState._internal();

  String? _loggedInEmail;
  List<Job> _assignedJobs = [];
  final DatabaseService _databaseService = DatabaseService();
  
  // Language settings
  String _languageCode = 'de';
  String get languageCode => _languageCode;
  
  /// Get current locale
  Locale get currentLocale => Locale(_languageCode);
  
  /// Set language and update the global notifier
  Future<void> setLanguage(String languageCode) async {
    if (supportedLanguageCodes.contains(languageCode)) {
      _languageCode = languageCode;
      await _databaseService.saveLanguagePreference(languageCode);
      localeNotifier.value = Locale(languageCode);
    }
  }
  
  /// Load language preference from database
  Future<void> loadLanguagePreference() async {
    final savedLanguage = await _databaseService.loadLanguagePreference();
    if (savedLanguage != null && supportedLanguageCodes.contains(savedLanguage)) {
      _languageCode = savedLanguage;
      localeNotifier.value = Locale(savedLanguage);
    }
  }

  // Serialize persistence to avoid overlapping DB save/load cycles
  bool _isPersistingJobs = false;
  List<Job>? _pendingJobs; // holds the latest jobs to persist if calls overlap

  // Jobs update notification (emitted once after DB was updated)
  final StreamController<void> _jobsUpdatedController = StreamController<void>.broadcast();
  Stream<void> get jobsUpdated => _jobsUpdatedController.stream;

  /// The logged-in user's email (used as local identifier for chats)
  String? get loggedInEmail => _loggedInEmail;

  List<Job> get assignedJobs => List.unmodifiable(_assignedJobs);

  void setLoggedInEmail(String email) {
    _loggedInEmail = email;
  }

  Future<void> clearLogin() async {
    _loggedInEmail = null;
    _assignedJobs.clear();
    // Clear database
    await _databaseService.clearAllData();
    // Notify listeners/UI that jobs were cleared
    _jobsUpdatedController.add(null);
    DartMQ().publish<void>(MQTopics.jobsUpdated, null);
  }

  bool get isLoggedIn => _loggedInEmail != null;

  Future<void> setAssignedJobs(List<Job> jobs) async {
    // Coalesce overlapping calls: if a persist is already running, remember only the latest
    if (_isPersistingJobs) {
      _pendingJobs = jobs;
      return;
    }

    _isPersistingJobs = true;
    try {
      // Start with the initial batch to persist
      var toPersist = jobs;
      while (true) {
        // Normalize first
        final normalized = toPersist.map((j) => j.normalized()).toList();

        // Persist normalized list to DB only (no UI notifications here)
        await _databaseService.saveJobs(normalized);

        // If another request came in during persistence, handle only the latest once
        if (_pendingJobs != null) {
          toPersist = _pendingJobs!;
          _pendingJobs = null;
          continue;
        }
        break;
      }
      // After DB is updated with the latest data, notify listeners once to refresh UI
      _jobsUpdatedController.add(null);
      // Also publish via dart_mq for app-wide decoupled messaging
      DartMQ().publish<void>(MQTopics.jobsUpdated, null);
    } finally {
      _isPersistingJobs = false;
    }
  }

  void addJob(Job job) {
    if (!_assignedJobs.contains(job)) {
      _assignedJobs.add(job);
    }
  }

  void removeJob(String jobId) {
    _assignedJobs.removeWhere((job) => job.id == jobId);
    // Update database
    _databaseService.saveJobs(_assignedJobs);
  }

  /// Delete a job by ID (called when server sends job_deleted event)
  Future<void> deleteJob(String jobId) async {
    _assignedJobs.removeWhere((job) => job.id == jobId);
    // Delete from database
    await _databaseService.deleteJob(jobId);
    // Notify listeners
    _jobsUpdatedController.add(null);
    DartMQ().publish<void>(MQTopics.jobsUpdated, null);
  }

  /// Add a new job (called when server sends job_created event)
  Future<void> addNewJob(Job job) async {
    // Check if job already exists
    if (_assignedJobs.any((j) => j.id == job.id)) {
      return;
    }
    // Add to memory
    _assignedJobs.insert(0, job);
    // Persist to database
    await _databaseService.saveOrUpdateJob(job);
    // Notify listeners
    _jobsUpdatedController.add(null);
    DartMQ().publish<void>(MQTopics.jobsUpdated, null);
  }

  /// Load login state from saved credentials on app start
  Future<void> loadLoginFromDatabase() async {
    final credentials = await _databaseService.loadCredentials();
    if (credentials != null) {
      _loggedInEmail = credentials.email;
    }
  }

  void updateJob(Job updatedJob) {
    final index = _assignedJobs.indexWhere((job) => job.id == updatedJob.id);
    if (index != -1) {
      _assignedJobs[index] = updatedJob;
    }
  }

  /// Refresh in-memory jobs from the database without emitting other notifications
  Future<void> refreshJobsFromDatabase() async {
    final jobs = await _databaseService.loadJobs();
    _assignedJobs = jobs;
  }

  /// Persistently upsert a single job and refresh in-memory list
  Future<void> upsertJob(Job job) async {
    await _databaseService.saveOrUpdateJob(job);
    final persisted = await _databaseService.loadJobs();
    _assignedJobs = persisted.isNotEmpty ? persisted : _assignedJobs;
  }
}
