/// Flutter Consent SDK
///
/// DSGVO/TTDSG-konformes Consent Management fuer Flutter Apps.
///
/// Nutzung:
/// 1. In main() mit ConsentManager.configure() initialisieren
/// 2. App mit ConsentProvider wrappen
/// 3. Mit ConsentGate Inhalte schuetzen
///
/// Copyright (c) 2025 BreakPilot
/// Apache License 2.0

library consent_sdk;

import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';

// =============================================================================
// Consent Categories
// =============================================================================

/// Standard-Consent-Kategorien nach IAB TCF 2.2
enum ConsentCategory {
  essential,    // Technisch notwendig
  functional,   // Personalisierung
  analytics,    // Nutzungsanalyse
  marketing,    // Werbung
  social,       // Social Media
}

// =============================================================================
// Consent State
// =============================================================================

/// Aktueller Consent-Zustand
class ConsentState {
  final Map<ConsentCategory, bool> categories;
  final Map<String, bool> vendors;
  final DateTime timestamp;
  final String version;
  final String? consentId;
  final DateTime? expiresAt;
  final String? tcfString;

  const ConsentState({
    required this.categories,
    this.vendors = const {},
    required this.timestamp,
    this.version = '1.0.0',
    this.consentId,
    this.expiresAt,
    this.tcfString,
  });

  /// Default State mit nur essential = true
  factory ConsentState.defaultState() {
    return ConsentState(
      categories: {
        ConsentCategory.essential: true,
        ConsentCategory.functional: false,
        ConsentCategory.analytics: false,
        ConsentCategory.marketing: false,
        ConsentCategory.social: false,
      },
      timestamp: DateTime.now(),
    );
  }

  ConsentState copyWith({
    Map<ConsentCategory, bool>? categories,
    Map<String, bool>? vendors,
    DateTime? timestamp,
    String? version,
    String? consentId,
    DateTime? expiresAt,
    String? tcfString,
  }) {
    return ConsentState(
      categories: categories ?? this.categories,
      vendors: vendors ?? this.vendors,
      timestamp: timestamp ?? this.timestamp,
      version: version ?? this.version,
      consentId: consentId ?? this.consentId,
      expiresAt: expiresAt ?? this.expiresAt,
      tcfString: tcfString ?? this.tcfString,
    );
  }

  Map<String, dynamic> toJson() => {
    'categories': categories.map((k, v) => MapEntry(k.name, v)),
    'vendors': vendors,
    'timestamp': timestamp.toIso8601String(),
    'version': version,
    'consentId': consentId,
    'expiresAt': expiresAt?.toIso8601String(),
    'tcfString': tcfString,
  };

  factory ConsentState.fromJson(Map<String, dynamic> json) {
    return ConsentState(
      categories: (json['categories'] as Map<String, dynamic>).map(
        (k, v) => MapEntry(
          ConsentCategory.values.firstWhere((e) => e.name == k),
          v as bool,
        ),
      ),
      vendors: Map<String, bool>.from(json['vendors'] ?? {}),
      timestamp: DateTime.parse(json['timestamp']),
      version: json['version'] ?? '1.0.0',
      consentId: json['consentId'],
      expiresAt: json['expiresAt'] != null
        ? DateTime.parse(json['expiresAt'])
        : null,
      tcfString: json['tcfString'],
    );
  }
}

// =============================================================================
// Configuration
// =============================================================================

/// SDK-Konfiguration
class ConsentConfig {
  final String apiEndpoint;
  final String siteId;
  final String language;
  final bool showRejectAll;
  final bool showAcceptAll;
  final bool granularControl;
  final int rememberDays;
  final bool debug;

  const ConsentConfig({
    required this.apiEndpoint,
    required this.siteId,
    this.language = 'en',
    this.showRejectAll = true,
    this.showAcceptAll = true,
    this.granularControl = true,
    this.rememberDays = 365,
    this.debug = false,
  });
}

// =============================================================================
// Consent Manager
// =============================================================================

/// Haupt-Manager fuer Consent-Verwaltung
class ConsentManager extends ChangeNotifier {
  // Singleton
  static ConsentManager? _instance;
  static ConsentManager get instance => _instance!;

  // State
  ConsentState _consent = ConsentState.defaultState();
  bool _isInitialized = false;
  bool _isLoading = true;
  bool _isBannerVisible = false;
  bool _isSettingsVisible = false;

  // Private
  ConsentConfig? _config;
  late ConsentStorage _storage;
  late ConsentApiClient _apiClient;

  // Getters
  ConsentState get consent => _consent;
  bool get isInitialized => _isInitialized;
  bool get isLoading => _isLoading;
  bool get isBannerVisible => _isBannerVisible;
  bool get isSettingsVisible => _isSettingsVisible;
  bool get needsConsent => _consent.consentId == null;

  // Private constructor
  ConsentManager._();

  /// Konfiguriert den ConsentManager
  /// Sollte in main() vor runApp() aufgerufen werden
  static Future<void> configure(ConsentConfig config) async {
    _instance = ConsentManager._();
    _instance!._config = config;
    _instance!._storage = ConsentStorage();
    _instance!._apiClient = ConsentApiClient(
      baseUrl: config.apiEndpoint,
      siteId: config.siteId,
    );

    if (config.debug) {
      debugPrint('[ConsentSDK] Configured with siteId: ${config.siteId}');
    }

    await _instance!._initialize();
  }

  // ==========================================================================
  // Initialization
  // ==========================================================================

  Future<void> _initialize() async {
    try {
      // Lokalen Consent laden
      final stored = await _storage.load();
      if (stored != null) {
        // Pruefen ob abgelaufen
        if (stored.expiresAt != null &&
            DateTime.now().isAfter(stored.expiresAt!)) {
          _consent = ConsentState.defaultState();
          await _storage.clear();
        } else {
          _consent = stored;
        }
      }

      // Vom Server synchronisieren
      try {
        final fingerprint = await DeviceFingerprint.generate();
        final serverConsent = await _apiClient.getConsent(fingerprint);
        if (serverConsent != null) {
          _consent = serverConsent;
          await _storage.save(_consent);
        }
      } catch (e) {
        if (_config?.debug == true) {
          debugPrint('[ConsentSDK] Failed to sync consent: $e');
        }
      }

      _isInitialized = true;

      // Banner anzeigen falls noetig
      if (needsConsent) {
        showBanner();
      }
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  // ==========================================================================
  // Public API
  // ==========================================================================

  /// Prueft ob Consent fuer Kategorie erteilt wurde
  bool hasConsent(ConsentCategory category) {
    // Essential ist immer erlaubt
    if (category == ConsentCategory.essential) return true;
    return _consent.categories[category] ?? false;
  }

  /// Alle Kategorien akzeptieren
  Future<void> acceptAll() async {
    final newCategories = {
      for (var cat in ConsentCategory.values) cat: true
    };
    final newConsent = _consent.copyWith(
      categories: newCategories,
      timestamp: DateTime.now(),
    );
    await _saveConsent(newConsent);
    hideBanner();
  }

  /// Alle nicht-essentiellen Kategorien ablehnen
  Future<void> rejectAll() async {
    final newCategories = {
      for (var cat in ConsentCategory.values)
        cat: cat == ConsentCategory.essential
    };
    final newConsent = _consent.copyWith(
      categories: newCategories,
      timestamp: DateTime.now(),
    );
    await _saveConsent(newConsent);
    hideBanner();
  }

  /// Auswahl speichern
  Future<void> saveSelection(Map<ConsentCategory, bool> categories) async {
    final updated = Map<ConsentCategory, bool>.from(categories);
    updated[ConsentCategory.essential] = true; // Essential immer true
    final newConsent = _consent.copyWith(
      categories: updated,
      timestamp: DateTime.now(),
    );
    await _saveConsent(newConsent);
    hideBanner();
  }

  // ==========================================================================
  // UI Control
  // ==========================================================================

  void showBanner() {
    _isBannerVisible = true;
    notifyListeners();
  }

  void hideBanner() {
    _isBannerVisible = false;
    notifyListeners();
  }

  void showSettings() {
    _isSettingsVisible = true;
    notifyListeners();
  }

  void hideSettings() {
    _isSettingsVisible = false;
    notifyListeners();
  }

  // ==========================================================================
  // Private Methods
  // ==========================================================================

  Future<void> _saveConsent(ConsentState newConsent) async {
    // Lokal speichern
    await _storage.save(newConsent);

    // An Server senden
    try {
      final fingerprint = await DeviceFingerprint.generate();
      final response = await _apiClient.saveConsent(newConsent, fingerprint);
      final updated = newConsent.copyWith(
        consentId: response['consentId'],
        expiresAt: DateTime.parse(response['expiresAt']),
      );
      _consent = updated;
      await _storage.save(updated);
    } catch (e) {
      // Lokal speichern auch bei Fehler
      _consent = newConsent;
      if (_config?.debug == true) {
        debugPrint('[ConsentSDK] Failed to sync consent: $e');
      }
    }

    notifyListeners();
  }
}

// =============================================================================
// Storage
// =============================================================================

/// Sichere Speicherung mit flutter_secure_storage
class ConsentStorage {
  final _storage = const FlutterSecureStorage();
  static const _key = 'breakpilot_consent_state';

  Future<ConsentState?> load() async {
    final data = await _storage.read(key: _key);
    if (data == null) return null;
    try {
      return ConsentState.fromJson(jsonDecode(data));
    } catch (e) {
      return null;
    }
  }

  Future<void> save(ConsentState consent) async {
    final data = jsonEncode(consent.toJson());
    await _storage.write(key: _key, value: data);
  }

  Future<void> clear() async {
    await _storage.delete(key: _key);
  }
}

// =============================================================================
// API Client
// =============================================================================

/// API Client fuer Backend-Kommunikation
class ConsentApiClient {
  final String baseUrl;
  final String siteId;

  ConsentApiClient({
    required this.baseUrl,
    required this.siteId,
  });

  Future<ConsentState?> getConsent(String fingerprint) async {
    final response = await http.get(
      Uri.parse('$baseUrl/banner/consent?site_id=$siteId&fingerprint=$fingerprint'),
    );

    if (response.statusCode != 200) return null;
    return ConsentState.fromJson(jsonDecode(response.body));
  }

  Future<Map<String, dynamic>> saveConsent(
    ConsentState consent,
    String fingerprint,
  ) async {
    final response = await http.post(
      Uri.parse('$baseUrl/banner/consent'),
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({
        'site_id': siteId,
        'device_fingerprint': fingerprint,
        'categories': consent.categories.map((k, v) => MapEntry(k.name, v)),
        'vendors': consent.vendors,
        'platform': Platform.isIOS ? 'ios' : 'android',
        'app_version': '1.0.0', // TODO: Get from package_info_plus
      }),
    );

    return jsonDecode(response.body);
  }
}

// =============================================================================
// Device Fingerprint
// =============================================================================

/// Privacy-konformer Device Fingerprint
class DeviceFingerprint {
  static Future<String> generate() async {
    final deviceInfo = DeviceInfoPlugin();
    String rawId;

    if (Platform.isIOS) {
      final iosInfo = await deviceInfo.iosInfo;
      rawId = '${iosInfo.identifierForVendor}-${iosInfo.model}-${iosInfo.systemVersion}';
    } else if (Platform.isAndroid) {
      final androidInfo = await deviceInfo.androidInfo;
      rawId = '${androidInfo.id}-${androidInfo.model}-${androidInfo.version.sdkInt}';
    } else {
      rawId = DateTime.now().millisecondsSinceEpoch.toString();
    }

    // SHA-256 Hash
    final bytes = utf8.encode(rawId);
    final digest = sha256.convert(bytes);
    return digest.toString();
  }
}

// =============================================================================
// Flutter Widgets
// =============================================================================

/// Consent Provider - Wraps the app
class ConsentProvider extends StatelessWidget {
  final Widget child;

  const ConsentProvider({
    super.key,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
      value: ConsentManager.instance,
      child: child,
    );
  }
}

/// Consent Gate - Zeigt Inhalt nur bei Consent
class ConsentGate extends StatelessWidget {
  final ConsentCategory category;
  final Widget child;
  final Widget? placeholder;
  final Widget? loading;

  const ConsentGate({
    super.key,
    required this.category,
    required this.child,
    this.placeholder,
    this.loading,
  });

  @override
  Widget build(BuildContext context) {
    return Consumer<ConsentManager>(
      builder: (context, consent, _) {
        if (consent.isLoading) {
          return loading ?? const SizedBox.shrink();
        }

        if (!consent.hasConsent(category)) {
          return placeholder ?? const SizedBox.shrink();
        }

        return child;
      },
    );
  }
}

/// Consent Banner - Default Banner UI
class ConsentBanner extends StatelessWidget {
  final String? title;
  final String? description;
  final String? acceptAllText;
  final String? rejectAllText;
  final String? settingsText;

  const ConsentBanner({
    super.key,
    this.title,
    this.description,
    this.acceptAllText,
    this.rejectAllText,
    this.settingsText,
  });

  @override
  Widget build(BuildContext context) {
    return Consumer<ConsentManager>(
      builder: (context, consent, _) {
        if (!consent.isBannerVisible) {
          return const SizedBox.shrink();
        }

        return Container(
          padding: const EdgeInsets.all(20),
          decoration: BoxDecoration(
            color: Theme.of(context).cardColor,
            borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
            boxShadow: [
              BoxShadow(
                color: Colors.black.withOpacity(0.1),
                blurRadius: 20,
                offset: const Offset(0, -5),
              ),
            ],
          ),
          child: SafeArea(
            top: false,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  title ?? 'Datenschutzeinstellungen',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
                const SizedBox(height: 12),
                Text(
                  description ??
                    'Wir nutzen Cookies und ähnliche Technologien, um Ihnen ein optimales Nutzererlebnis zu bieten.',
                  style: Theme.of(context).textTheme.bodyMedium,
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 20),
                Row(
                  children: [
                    Expanded(
                      child: OutlinedButton(
                        onPressed: consent.rejectAll,
                        child: Text(rejectAllText ?? 'Alle ablehnen'),
                      ),
                    ),
                    const SizedBox(width: 8),
                    Expanded(
                      child: OutlinedButton(
                        onPressed: consent.showSettings,
                        child: Text(settingsText ?? 'Einstellungen'),
                      ),
                    ),
                    const SizedBox(width: 8),
                    Expanded(
                      child: FilledButton(
                        onPressed: consent.acceptAll,
                        child: Text(acceptAllText ?? 'Alle akzeptieren'),
                      ),
                    ),
                  ],
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

/// Consent Placeholder - Placeholder fuer blockierten Inhalt
class ConsentPlaceholder extends StatelessWidget {
  final ConsentCategory category;
  final String? message;
  final String? buttonText;

  const ConsentPlaceholder({
    super.key,
    required this.category,
    this.message,
    this.buttonText,
  });

  String get _categoryName {
    switch (category) {
      case ConsentCategory.essential:
        return 'Essentielle Cookies';
      case ConsentCategory.functional:
        return 'Funktionale Cookies';
      case ConsentCategory.analytics:
        return 'Statistik-Cookies';
      case ConsentCategory.marketing:
        return 'Marketing-Cookies';
      case ConsentCategory.social:
        return 'Social Media-Cookies';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surfaceVariant,
        borderRadius: BorderRadius.circular(12),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            message ?? 'Dieser Inhalt erfordert $_categoryName.',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 12),
          OutlinedButton(
            onPressed: ConsentManager.instance.showSettings,
            child: Text(buttonText ?? 'Cookie-Einstellungen öffnen'),
          ),
        ],
      ),
    );
  }
}

// =============================================================================
// Extension for easy context access
// =============================================================================

extension ConsentExtension on BuildContext {
  ConsentManager get consent => Provider.of<ConsentManager>(this, listen: false);

  bool hasConsent(ConsentCategory category) => consent.hasConsent(category);
}
