import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'dart:io' show Platform;
import '../models/subscription_model.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

class NotificationService {
  static final FlutterLocalNotificationsPlugin _notifications =
      FlutterLocalNotificationsPlugin();
  static final _secureStorage = const FlutterSecureStorage();

  static const _notificationEnabledKey = 'notification_enabled';
  static const _paymentNotificationEnabledKey = 'payment_notification_enabled';
  static const _reminderDaysKey = 'reminder_days';
  static const _reminderHourKey = 'reminder_hour';
  static const _reminderMinuteKey = 'reminder_minute';
  static const _dailyReminderKey = 'daily_reminder_enabled';

  // 초기화 상태를 추적하기 위한 플래그
  static bool _initialized = false;

  // 웹 플랫폼 여부 확인 (웹에서는 flutter_local_notifications가 지원되지 않음)
  static bool get _isWeb => kIsWeb;

  static const AndroidNotificationChannel channel = AndroidNotificationChannel(
    'subscription_channel',
    'Subscription Notifications',
    description: 'Channel for subscription reminders',
    importance: Importance.high,
    enableVibration: true,
  );

  // 알림 초기화
  static Future<void> init() async {
    try {
      // 웹 플랫폼인 경우 초기화 건너뛰기
      if (_isWeb) {
        debugPrint('웹 플랫폼에서는 로컬 알림이 지원되지 않습니다.');
        return;
      }

      tz.initializeTimeZones();
      try {
        tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
      } catch (e) {
        // 타임존 찾기 실패시 UTC 사용
        tz.setLocalLocation(tz.UTC);
        debugPrint('타임존 설정 실패, UTC 사용: $e');
      }
      const androidSettings =
          AndroidInitializationSettings('@mipmap/ic_launcher');
      const iosSettings = DarwinInitializationSettings();
      const initSettings =
          InitializationSettings(android: androidSettings, iOS: iosSettings);

      await _notifications.initialize(initSettings);
      _initialized = true;
      debugPrint('알림 서비스 초기화 완료');
    } catch (e) {
      _initialized = false;
      debugPrint('알림 서비스 초기화 실패: $e');
    }
  }

  static Future<bool> isNotificationEnabled() async {
    final value = await _secureStorage.read(key: _notificationEnabledKey);
    return value == 'true';
  }

  static Future<bool> isPaymentNotificationEnabled() async {
    final value =
        await _secureStorage.read(key: _paymentNotificationEnabledKey);
    return value == 'true';
  }

  static Future<void> setNotificationEnabled(bool value) async {
    await _secureStorage.write(
      key: _notificationEnabledKey,
      value: value.toString(),
    );
  }

  static Future<void> setPaymentNotificationEnabled(bool value) async {
    await _secureStorage.write(
      key: _paymentNotificationEnabledKey,
      value: value.toString(),
    );
  }

  // 알림 시점 설정 (1일전, 2일전, 3일전) 가져오기
  static Future<int> getReminderDays() async {
    final value = await _secureStorage.read(key: _reminderDaysKey);
    return value != null ? int.tryParse(value) ?? 3 : 3;
  }

  // 알림 시간 가져오기
  static Future<int> getReminderHour() async {
    final value = await _secureStorage.read(key: _reminderHourKey);
    return value != null ? int.tryParse(value) ?? 10 : 10;
  }

  static Future<int> getReminderMinute() async {
    final value = await _secureStorage.read(key: _reminderMinuteKey);
    return value != null ? int.tryParse(value) ?? 0 : 0;
  }

  // 반복 알림 설정 가져오기
  static Future<bool> isDailyReminderEnabled() async {
    final value = await _secureStorage.read(key: _dailyReminderKey);
    return value == 'true';
  }

  // 모든 구독의 알림 일정 다시 예약
  static Future<void> reschedulAllNotifications(
      List<SubscriptionModel> subscriptions) async {
    try {
      // 웹 플랫폼이거나 알림 초기화 실패한 경우 건너뛰기
      if (_isWeb || !_initialized) {
        debugPrint('웹 플랫폼이거나 알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
        return;
      }

      // 기존 알림 모두 취소
      await cancelAllNotifications();

      // 알림 설정 가져오기
      final isPaymentEnabled = await isPaymentNotificationEnabled();
      if (!isPaymentEnabled) return;

      final reminderDays = await getReminderDays();
      final reminderHour = await getReminderHour();
      final reminderMinute = await getReminderMinute();
      final isDailyReminder = await isDailyReminderEnabled();

      // 각 구독에 대해 알림 재설정
      for (final subscription in subscriptions) {
        await schedulePaymentReminder(
          subscription: subscription,
          reminderDays: reminderDays,
          reminderHour: reminderHour,
          reminderMinute: reminderMinute,
          isDailyReminder: isDailyReminder,
        );
      }
    } catch (e) {
      debugPrint('알림 일정 재설정 중 오류 발생: $e');
    }
  }


  static Future<bool> requestPermission() async {
    // 웹 플랫폼인 경우 false 반환
    if (_isWeb) return false;
    
    // iOS 처리
    if (Platform.isIOS) {
      final iosImplementation = _notifications
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>();
      
      if (iosImplementation != null) {
        final granted = await iosImplementation.requestPermissions(
          alert: true,
          badge: true,
          sound: true,
        );
        return granted ?? false;
      }
    }
    
    // Android 처리
    if (Platform.isAndroid) {
      final androidImplementation = _notifications
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>();
      
      if (androidImplementation != null) {
        final granted = await androidImplementation
            .requestNotificationsPermission();
        return granted ?? false;
      }
    }
    
    return false;
  }

  // 권한 상태 확인
  static Future<bool> checkPermission() async {
    // 웹 플랫폼인 경우 false 반환
    if (_isWeb) return false;
    
    // Android 처리
    if (Platform.isAndroid) {
      final androidImplementation = _notifications
          .resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>();
      
      if (androidImplementation != null) {
        // Android 13 이상에서만 권한 확인 필요
        final isEnabled = await androidImplementation.areNotificationsEnabled();
        return isEnabled ?? false;
      }
    }
    
    // iOS 처리
    if (Platform.isIOS) {
      final iosImplementation = _notifications
          .resolvePlatformSpecificImplementation<
              IOSFlutterLocalNotificationsPlugin>();
      
      if (iosImplementation != null) {
        final settings = await iosImplementation.checkPermissions();
        return settings?.isEnabled ?? false;
      }
    }
    
    return true; // 기본값
  }

  // 알림 스케줄 설정
  static Future<void> scheduleNotification({
    required int id,
    required String title,
    required String body,
    required DateTime scheduledDate,
  }) async {
    // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
    if (_isWeb || !_initialized) {
      debugPrint('알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
      return;
    }
    
    try {
      const androidDetails = AndroidNotificationDetails(
        'subscription_channel',
        '구독 알림',
        channelDescription: '구독 관련 알림을 보여줍니다.',
        importance: Importance.high,
        priority: Priority.high,
      );

      final iosDetails = const DarwinNotificationDetails();
      
      // tz.local 초기화 확인 및 재시도
      tz.Location location;
      try {
        location = tz.local;
      } catch (e) {
        // tz.local이 초기화되지 않은 경우 재시도
        debugPrint('tz.local 초기화되지 않음, 재시도 중...');
        try {
          tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
          location = tz.local;
        } catch (_) {
          // 그래도 실패하면 UTC 사용
          debugPrint('타임존 설정 실패, UTC 사용');
          tz.setLocalLocation(tz.UTC);
          location = tz.UTC;
        }
      }

      await _notifications.zonedSchedule(
        id,
        title,
        body,
        tz.TZDateTime.from(scheduledDate, location),
        NotificationDetails(android: androidDetails, iOS: iosDetails),
        androidAllowWhileIdle: true,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
      );
    } catch (e) {
      debugPrint('알림 예약 중 오류 발생: $e');
    }
  }

  // 알림 취소
  static Future<void> cancelNotification(int id) async {
    await _notifications.cancel(id);
  }

  // 모든 알림 취소
  static Future<void> cancelAllNotifications() async {
    try {
      // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
      if (_isWeb || !_initialized) {
        debugPrint('웹 플랫폼이거나 알림 서비스가 초기화되지 않아 취소할 수 없습니다.');
        return;
      }

      await _notifications.cancelAll();
      debugPrint('모든 알림이 취소되었습니다.');
    } catch (e) {
      debugPrint('알림 취소 중 오류 발생: $e');
    }
  }

  static Future<void> scheduleSubscriptionNotification(
      SubscriptionModel subscription) async {
    // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
    if (_isWeb || !_initialized) {
      debugPrint('알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
      return;
    }
    
    try {
      final notificationId = subscription.id.hashCode;

      const androidDetails = AndroidNotificationDetails(
        'subscription_channel',
        '구독 알림',
        channelDescription: '구독 만료 알림을 보내는 채널입니다.',
        importance: Importance.high,
        priority: Priority.high,
      );

      const iosDetails = DarwinNotificationDetails(
        presentAlert: true,
        presentBadge: true,
        presentSound: true,
      );

      const notificationDetails = NotificationDetails(
        android: androidDetails,
        iOS: iosDetails,
      );
      
      // tz.local 초기화 확인 및 재시도
      tz.Location location;
      try {
        location = tz.local;
      } catch (e) {
        // tz.local이 초기화되지 않은 경우 재시도
        debugPrint('tz.local 초기화되지 않음, 재시도 중...');
        try {
          tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
          location = tz.local;
        } catch (_) {
          // 그래도 실패하면 UTC 사용
          debugPrint('타임존 설정 실패, UTC 사용');
          tz.setLocalLocation(tz.UTC);
          location = tz.UTC;
        }
      }

      await _notifications.zonedSchedule(
        notificationId,
        '구독 만료 알림',
        '${subscription.serviceName} 구독이 ${subscription.nextBillingDate.day}일 만료됩니다.',
        tz.TZDateTime.from(subscription.nextBillingDate, location),
        notificationDetails,
        androidAllowWhileIdle: true,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
      );
    } catch (e) {
      debugPrint('구독 알림 예약 중 오류 발생: $e');
    }
  }

  static Future<void> updateSubscriptionNotification(
      SubscriptionModel subscription) async {
    await cancelSubscriptionNotification(subscription);
    await scheduleSubscriptionNotification(subscription);
  }

  static Future<void> cancelSubscriptionNotification(
      SubscriptionModel subscription) async {
    final notificationId = subscription.id.hashCode;
    await _notifications.cancel(notificationId);
  }

  static Future<void> schedulePaymentNotification(
      SubscriptionModel subscription) async {
    // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
    if (_isWeb || !_initialized) {
      debugPrint('알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
      return;
    }
    
    try {
      final paymentDate = subscription.nextBillingDate;
      final reminderDate = paymentDate.subtract(const Duration(days: 3));
      
      // tz.local 초기화 확인 및 재시도
      tz.Location location;
      try {
        location = tz.local;
      } catch (e) {
        // tz.local이 초기화되지 않은 경우 재시도
        debugPrint('tz.local 초기화되지 않음, 재시도 중...');
        try {
          tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
          location = tz.local;
        } catch (_) {
          // 그래도 실패하면 UTC 사용
          debugPrint('타임존 설정 실패, UTC 사용');
          tz.setLocalLocation(tz.UTC);
          location = tz.UTC;
        }
      }

      await _notifications.zonedSchedule(
        subscription.id.hashCode,
        '구독 결제 예정 알림',
        '${subscription.serviceName} 결제가 3일 후 예정되어 있습니다.',
        tz.TZDateTime.from(reminderDate, location),
        const NotificationDetails(
          android: AndroidNotificationDetails(
            'payment_channel',
            'Payment Notifications',
            channelDescription: 'Channel for subscription payment reminders',
            importance: Importance.high,
            priority: Priority.high,
          ),
        ),
        androidAllowWhileIdle: true,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
      );
    } catch (e) {
      debugPrint('결제 알림 예약 중 오류 발생: $e');
    }
  }

  static Future<void> scheduleExpirationNotification(
      SubscriptionModel subscription) async {
    // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
    if (_isWeb || !_initialized) {
      debugPrint('알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
      return;
    }
    
    try {
      final expirationDate = subscription.nextBillingDate;
      final reminderDate = expirationDate.subtract(const Duration(days: 7));
      
      // tz.local 초기화 확인 및 재시도
      tz.Location location;
      try {
        location = tz.local;
      } catch (e) {
        // tz.local이 초기화되지 않은 경우 재시도
        debugPrint('tz.local 초기화되지 않음, 재시도 중...');
        try {
          tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
          location = tz.local;
        } catch (_) {
          // 그래도 실패하면 UTC 사용
          debugPrint('타임존 설정 실패, UTC 사용');
          tz.setLocalLocation(tz.UTC);
          location = tz.UTC;
        }
      }

      await _notifications.zonedSchedule(
        (subscription.id + '_expiration').hashCode,
        '구독 만료 예정 알림',
        '${subscription.serviceName} 구독이 7일 후 만료됩니다.',
        tz.TZDateTime.from(reminderDate, location),
        const NotificationDetails(
          android: AndroidNotificationDetails(
            'expiration_channel',
            'Expiration Notifications',
            channelDescription: 'Channel for subscription expiration reminders',
            importance: Importance.high,
            priority: Priority.high,
          ),
        ),
        androidAllowWhileIdle: true,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
      );
    } catch (e) {
      debugPrint('만료 알림 예약 중 오류 발생: $e');
    }
  }

  static Future<void> schedulePaymentReminder({
    required SubscriptionModel subscription,
    int reminderDays = 3,
    int reminderHour = 10,
    int reminderMinute = 0,
    bool isDailyReminder = false,
  }) async {
    // 웹 플랫폼이거나 초기화되지 않은 경우 건너뛰기
    if (_isWeb || !_initialized) {
      debugPrint('웹 플랫폼이거나 알림 서비스가 초기화되지 않아 알림을 예약할 수 없습니다.');
      return;
    }

    try {
      // tz.local 초기화 확인 및 재시도
      tz.Location location;
      try {
        location = tz.local;
      } catch (e) {
        // tz.local이 초기화되지 않은 경우 재시도
        debugPrint('tz.local 초기화되지 않음, 재시도 중...');
        try {
          tz.setLocalLocation(tz.getLocation('Asia/Seoul'));
          location = tz.local;
        } catch (_) {
          // 그래도 실패하면 UTC 사용
          debugPrint('타임존 설정 실패, UTC 사용');
          tz.setLocalLocation(tz.UTC);
          location = tz.UTC;
        }
      }
      
      // 기본 알림 예약 (지정된 일수 전)
      final scheduledDate =
          subscription.nextBillingDate.subtract(Duration(days: reminderDays)).copyWith(
                hour: reminderHour,
                minute: reminderMinute,
                second: 0,
                millisecond: 0,
                microsecond: 0,
              );

      // 남은 일수에 따른 메시지 생성
      String daysText = '$reminderDays일 후';
      if (reminderDays == 1) {
        daysText = '내일';
      }

      // 이벤트 종료로 인한 가격 변동 확인
      String notificationBody;
      if (subscription.isEventActive && 
          subscription.eventEndDate != null &&
          subscription.eventEndDate!.isBefore(subscription.nextBillingDate) &&
          subscription.eventEndDate!.isAfter(DateTime.now())) {
        // 이벤트가 결제일 전에 종료되는 경우
        final eventPrice = subscription.eventPrice ?? subscription.monthlyCost;
        final normalPrice = subscription.monthlyCost;
        notificationBody = '${subscription.serviceName} 구독료 ${normalPrice.toStringAsFixed(0)}원이 $daysText 결제 예정입니다.\n'
                          '⚠️ 이벤트 종료로 가격이 ${eventPrice.toStringAsFixed(0)}원에서 ${normalPrice.toStringAsFixed(0)}원으로 변경됩니다.';
      } else {
        // 일반 알림
        final currentPrice = subscription.currentPrice;
        notificationBody = '${subscription.serviceName} 구독료 ${currentPrice.toStringAsFixed(0)}원이 $daysText 결제 예정입니다.';
      }

      await _notifications.zonedSchedule(
        subscription.id.hashCode,
        '구독 결제 예정 알림',
        notificationBody,
        tz.TZDateTime.from(scheduledDate, location),
        const NotificationDetails(
          android: AndroidNotificationDetails(
            'subscription_channel',
            'Subscription Notifications',
            channelDescription: 'Channel for subscription reminders',
            importance: Importance.high,
            priority: Priority.high,
          ),
          iOS: DarwinNotificationDetails(),
        ),
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
      );

      // 매일 반복 알림 설정 (2일 이상 전에 알림 시작 & 반복 알림 활성화된 경우)
      if (isDailyReminder && reminderDays >= 2) {
        // 첫 번째 알림 다음 날부터 결제일 전날까지 매일 알림 예약
        for (int i = reminderDays - 1; i >= 1; i--) {
          final dailyDate = subscription.nextBillingDate.subtract(Duration(days: i)).copyWith(
                hour: reminderHour,
                minute: reminderMinute,
                second: 0,
                millisecond: 0,
                microsecond: 0,
              );

          // 남은 일수에 따른 메시지 생성
          String remainingDaysText = '$i일 후';
          if (i == 1) {
            remainingDaysText = '내일';
          }

          // 각 날짜에 대한 이벤트 종료 확인
          String dailyNotificationBody;
          if (subscription.isEventActive && 
              subscription.eventEndDate != null &&
              subscription.eventEndDate!.isBefore(subscription.nextBillingDate) &&
              subscription.eventEndDate!.isAfter(DateTime.now())) {
            final eventPrice = subscription.eventPrice ?? subscription.monthlyCost;
            final normalPrice = subscription.monthlyCost;
            dailyNotificationBody = '${subscription.serviceName} 구독료 ${normalPrice.toStringAsFixed(0)}원이 $remainingDaysText 결제 예정입니다.\n'
                              '⚠️ 이벤트 종료로 가격이 ${eventPrice.toStringAsFixed(0)}원에서 ${normalPrice.toStringAsFixed(0)}원으로 변경됩니다.';
          } else {
            final currentPrice = subscription.currentPrice;
            dailyNotificationBody = '${subscription.serviceName} 구독료 ${currentPrice.toStringAsFixed(0)}원이 $remainingDaysText 결제 예정입니다.';
          }

          await _notifications.zonedSchedule(
            subscription.id.hashCode + i, // 고유한 ID 생성을 위해 날짜 차이 더함
            '구독 결제 예정 알림',
            dailyNotificationBody,
            tz.TZDateTime.from(dailyDate, location),
            const NotificationDetails(
              android: AndroidNotificationDetails(
                'subscription_channel',
                'Subscription Notifications',
                channelDescription: 'Channel for subscription reminders',
                importance: Importance.high,
                priority: Priority.high,
              ),
              iOS: DarwinNotificationDetails(),
            ),
            uiLocalNotificationDateInterpretation:
                UILocalNotificationDateInterpretation.absoluteTime,
            androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
          );
        }
      }
    } catch (e) {
      debugPrint('결제 알림 예약 중 오류 발생: $e');
    }
  }

  static String getNotificationBody(String serviceName, double amount) {
    return '$serviceName 구독료 ${amount.toStringAsFixed(0)}원이 결제되었습니다.';
  }
}
