import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

class SavedDevice {
  const SavedDevice({
    required this.ip,
    required this.port,
    required this.name,
    required this.lastUsedAt,
  });

  final String ip;
  final int port;
  final String name;
  final DateTime lastUsedAt;

  String get address => '$ip:$port';

  Map<String, dynamic> toJson() {
    return <String, dynamic>{
      'ip': ip,
      'port': port,
      'name': name,
      'lastUsedAt': lastUsedAt.toIso8601String(),
    };
  }

  factory SavedDevice.fromJson(Map<String, dynamic> json) {
    return SavedDevice(
      ip: _normalizeIp(json['ip']?.toString()),
      port: _normalizePort(json['port']),
      name: _normalizeName(json['name']?.toString()),
      lastUsedAt: DateTime.tryParse(json['lastUsedAt']?.toString() ?? '') ??
          DateTime.fromMillisecondsSinceEpoch(0),
    );
  }

  static String _normalizeIp(String? value) {
    final normalized = value?.trim() ?? '';
    return normalized.isEmpty ? '127.0.0.1' : normalized;
  }

  static int _normalizePort(dynamic value) {
    final port = int.tryParse(value?.toString() ?? '');
    if (port == null || port <= 0 || port > 65535) {
      return 5000;
    }
    return port;
  }

  static String _normalizeName(String? value) {
    final normalized = value?.trim() ?? '';
    return normalized.isEmpty ? 'Showen' : normalized;
  }
}

class DeviceStorageService {
  static const String _devicesKey = 'showen_device_list';
  static const int _maxDevices = 10;

  SharedPreferences? _preferences;

  Future<void> saveDevice(String ip, int port, String? name) async {
    final prefs = await _getPreferences();
    final devices = await getDevices();
    final normalizedIp = _normalizeIp(ip);
    final normalizedPort = _normalizePort(port);
    final normalizedName = _normalizeName(name);
    final now = DateTime.now();

    final nextDevices = <SavedDevice>[
      SavedDevice(
        ip: normalizedIp,
        port: normalizedPort,
        name: normalizedName,
        lastUsedAt: now,
      ),
      ...devices.where(
        (device) => !(device.ip == normalizedIp && device.port == normalizedPort),
      ),
    ]
      ..sort((a, b) => b.lastUsedAt.compareTo(a.lastUsedAt));

    await prefs.setString(
      _devicesKey,
      jsonEncode(
        nextDevices
            .take(_maxDevices)
            .map((device) => device.toJson())
            .toList(growable: false),
      ),
    );
  }

  Future<List<SavedDevice>> getDevices() async {
    final prefs = await _getPreferences();
    final raw = prefs.getString(_devicesKey);
    if (raw == null || raw.isEmpty) {
      return const <SavedDevice>[];
    }

    try {
      final decoded = jsonDecode(raw);
      if (decoded is! List) {
        return const <SavedDevice>[];
      }

      final devices = decoded
          .whereType<Map>()
          .map((item) => SavedDevice.fromJson(Map<String, dynamic>.from(item)))
          .toList(growable: false)
        ..sort((a, b) => b.lastUsedAt.compareTo(a.lastUsedAt));

      return devices.take(_maxDevices).toList(growable: false);
    } on FormatException {
      return const <SavedDevice>[];
    }
  }

  Future<void> removeDevice(String ip, [int? port]) async {
    final prefs = await _getPreferences();
    final normalizedIp = _normalizeIp(ip);
    final nextDevices = (await getDevices())
        .where(
          (device) =>
              device.ip != normalizedIp ||
              (port != null && device.port != _normalizePort(port)),
        )
        .map((device) => device.toJson())
        .toList(growable: false);

    await prefs.setString(_devicesKey, jsonEncode(nextDevices));
  }

  Future<SavedDevice?> getLastDevice() async {
    final devices = await getDevices();
    if (devices.isEmpty) {
      return null;
    }
    return devices.first;
  }

  Future<SharedPreferences> _getPreferences() async {
    return _preferences ??= await SharedPreferences.getInstance();
  }

  String _normalizeIp(String value) => SavedDevice._normalizeIp(value);

  int _normalizePort(dynamic value) => SavedDevice._normalizePort(value);

  String _normalizeName(String? value) => SavedDevice._normalizeName(value);
}
