import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import '../models/client.dart';

class CrmApiService {
  static const String _base = 'https://api.doormile.com/api/v1';

  final http.Client _http;
  String? _authToken;

  CrmApiService({http.Client? client}) : _http = client ?? http.Client();

  void setToken(String token) {
    _authToken = token;
  }

  Future<Map<String, String>> _getHeaders() async {
    final Map<String, String> headers = {
      'Content-Type': 'application/json',
    };
    if (_authToken != null && _authToken!.isNotEmpty) {
      headers['Authorization'] = 'Bearer $_authToken';
    } else {
      // Fallback to shared preferences just in case
      try {
        final prefs = await SharedPreferences.getInstance();
        final userData = prefs.getString('auth_user_data');
        if (userData != null && userData.isNotEmpty) {
          final token = jsonDecode(userData)['token']?.toString();
          if (token != null && token.isNotEmpty) {
            headers['Authorization'] = 'Bearer $token';
          }
        }
      } catch (_) {}
    }
    return headers;
  }

  // ── Public API ──────────────────────────────────────────────────────────────

  /// Fetch all clients from the backend.
  /// Throws on network failure so the caller can distinguish "backend down"
  /// from "backend returned an empty list".
  Future<List<Client>> loadClients() async {
    try {
      final res = await _http
          .get(Uri.parse('$_base/crm/clients?limit=1000'), headers: await _getHeaders())
          .timeout(const Duration(seconds: 10));
      if (res.statusCode != 200) {
        debugPrint('loadClients failed: ${res.statusCode} ${res.body}');
        return [];
      }
      final decoded = jsonDecode(res.body);
      List<dynamic> body = [];
      if (decoded is List) {
        body = decoded;
      } else if (decoded is Map && decoded['data'] is List) {
        body = decoded['data'];
      }
      return body.map((j) => _fromJson(j as Map<String, dynamic>)).toList();
    } catch (e) {
      debugPrint('loadClients exception: $e');
      return [];
    }
  }

  /// Create a new client on the backend. Returns the server-assigned record,
  /// or null if the call fails (caller keeps the locally-created record).
  Future<Client?> createClient(Client client) async {
    try {
      final res = await _http
          .post(
            Uri.parse('$_base/crm/clients'),
            headers: await _getHeaders(),
            body: jsonEncode(_toCreatePayload(client)),
          )
          .timeout(const Duration(seconds: 10));
      if (res.statusCode != 201) return null;
      return _fromJson(jsonDecode(res.body) as Map<String, dynamic>);
    } catch (_) {
      return null;
    }
  }

  /// Update an existing client. `client.id` must be a valid numeric backend ID.
  Future<Client?> updateClient(Client client) async {
    final numId = int.tryParse(client.id);
    if (numId == null) return null; // temp local ID — not yet persisted
    try {
      final res = await _http
          .put(
            Uri.parse('$_base/crm/clients/$numId'),
            headers: await _getHeaders(),
            body: jsonEncode(_toUpdatePayload(client)),
          )
          .timeout(const Duration(seconds: 10));
      if (res.statusCode != 200) return null;
      return _fromJson(jsonDecode(res.body) as Map<String, dynamic>);
    } catch (_) {
      return null;
    }
  }

  /// Delete a client by ID. Returns true if successful.
  Future<bool> deleteClient(String clientId) async {
    final numId = int.tryParse(clientId);
    if (numId == null) return false;
    try {
      final res = await _http
          .delete(
            Uri.parse('$_base/crm/clients/$numId'),
            headers: await _getHeaders(),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200;
    } catch (_) {
      return false;
    }
  }

  /// Fetch all competitor branch surveys
  Future<List<dynamic>> fetchSurveys() async {
    try {
      final res = await _http
          .get(Uri.parse('$_base/admin/competitor-branches?limit=1000'), headers: await _getHeaders())
          .timeout(const Duration(seconds: 10));
      if (res.statusCode != 200) return [];
      final data = jsonDecode(res.body);
      if (data is List) return data;
      if (data is Map && data['data'] is List) return data['data'];
      return [];
    } catch (_) {
      return [];
    }
  }

  /// Fetch carrier pricing
  Future<List<dynamic>> fetchPricing() async {
    try {
      final res = await _http
          .get(Uri.parse('$_base/admin/carrier-pricing?limit=1000'), headers: await _getHeaders())
          .timeout(const Duration(seconds: 10));
      if (res.statusCode != 200) return [];
      final data = jsonDecode(res.body);
      if (data is List) return data;
      if (data is Map && data['data'] is List) return data['data'];
      return [];
    } catch (_) {
      return [];
    }
  }

  /// Create carrier pricing
  Future<bool> createPricing(Map<String, dynamic> data) async {
    try {
      final res = await _http
          .post(
            Uri.parse('$_base/admin/carrier-pricing'),
            headers: await _getHeaders(),
            body: jsonEncode({
              'company': data['company'],
              'weight_slab': data['weight_slab'],
              'zone': data['zone'] ?? '',
              'service_type': data['service_type'] ?? '',
              'rate': data['rate'].toString(),
            }),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 201 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  /// Update carrier pricing
  Future<bool> updatePricing(String id, Map<String, dynamic> data) async {
    try {
      final res = await _http
          .put(
            Uri.parse('$_base/admin/carrier-pricing/$id'),
            headers: await _getHeaders(),
            body: jsonEncode({
              'company': data['company'],
              'weight_slab': data['weight_slab'],
              'zone': data['zone'] ?? '',
              'service_type': data['service_type'] ?? '',
              'rate': data['rate'].toString(),
            }),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  /// Delete carrier pricing
  Future<bool> deletePricing(String id) async {
    try {
      final res = await _http
          .delete(
            Uri.parse('$_base/admin/carrier-pricing/$id'),
            headers: await _getHeaders(),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  /// Create competitor branch (survey)
  Future<bool> createSurvey(Map<String, dynamic> data) async {
    try {
      final res = await _http
          .post(
            Uri.parse('$_base/admin/competitor-branches'),
            headers: await _getHeaders(),
            body: jsonEncode(data),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 201 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  /// Update competitor branch (survey)
  Future<bool> updateSurvey(String id, Map<String, dynamic> data) async {
    try {
      final res = await _http
          .put(
            Uri.parse('$_base/admin/competitor-branches/$id'),
            headers: await _getHeaders(),
            body: jsonEncode(data),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  /// Delete competitor branch (survey)
  Future<bool> deleteSurvey(String id) async {
    try {
      final res = await _http
          .delete(
            Uri.parse('$_base/admin/competitor-branches/$id'),
            headers: await _getHeaders(),
          )
          .timeout(const Duration(seconds: 10));
      return res.statusCode == 200 || res.statusCode == 204;
    } catch (_) {
      return false;
    }
  }

  // ── Serialisation ───────────────────────────────────────────────────────────

  Client _fromJson(Map<String, dynamic> j) => Client(
        id: j['id'].toString(),
        name: _joinName(j['first_name'], j['last_name']),
        city: j['city'] as String? ?? '',
        businessType: _parseBusinessType(j['businessType'] as String?),
        parcelVolume: (j['parcelVolume'] as num?)?.toDouble() ?? 0.0,
        activeContracts: j['activeContracts'] as int? ?? 0,
        provider: j['provider'] as String? ?? '',
        efficiency: j['efficiency'] as String? ?? '',
        status: _parseStatus(j['status'] as String?),
        lastUpdated: j['lastUpdated'] as String? ?? '',
        surveySubmitted: true, // in DB = was submitted
        notes: j['notes'] as String? ?? '',
        dataConsent: _parseConsent(j['dataConsent'] as String?),
        phone: j['phone'] as String? ?? '',
        frequency: j['frequency'] as String? ?? '',
        businessState: j['businessState'] as String? ?? '',
        transitFrom: j['transitFrom'] as String? ?? '',
        transitTo: j['transitTo'] as String? ?? '',
        neighbourhood: j['neighbourhood'] as String? ?? '',
        logisticsSegment: j['logisticsSegment'] as String? ?? '',
        pincode: j['pincode'] as String? ?? '',
        surveyLat: (j['survey_lat'] as num?)?.toDouble() ?? 0.0,
        surveyLng: (j['survey_long'] as num?)?.toDouble() ?? 0.0,
        surveyAddress: j['surveyAddress'] as String? ?? '',
        surveyZone: j['surveyZone'] as String? ?? '',
        surveyPincode: j['surveyPincode'] as String? ?? '',
        recordedByName: '',
        recordedByEmail: j['email'] as String? ?? '',
        recordedByRole: j['role'] as String? ?? '',
      );

  Map<String, dynamic> _toCreatePayload(Client c) => {
        'first_name': c.name,
        'phone': c.phone,
        'city': c.city,
        'businessState': c.businessState,
        'businessType': c.businessType.name,
        'status': c.status.name,
        'frequency': c.frequency,
        'parcelVolume': c.parcelVolume,
        'activeContracts': c.activeContracts,
        'provider': c.provider,
        'efficiency': c.efficiency,
        'logisticsSegment': c.logisticsSegment,
        'transitFrom': c.transitFrom,
        'transitTo': c.transitTo,
        'neighbourhood': c.neighbourhood,
        'pincode': c.pincode,
        'surveyAddress': c.surveyAddress,
        'surveyZone': c.surveyZone,
        'surveyPincode': c.surveyPincode,
        'survey_lat': c.surveyLat,
        'survey_long': c.surveyLng,
        'notes': c.notes,
        'dataConsent': c.dataConsent.name,
        'registration_source': 'mobile',
      };

  Map<String, dynamic> _toUpdatePayload(Client c) => {
        'first_name': c.name,
        'phone': c.phone,
        'city': c.city,
        'businessState': c.businessState,
        'businessType': c.businessType.name,
        'status': c.status.name,
        'frequency': c.frequency,
        'parcelVolume': c.parcelVolume,
        'activeContracts': c.activeContracts,
        'provider': c.provider,
        'efficiency': c.efficiency,
        'logisticsSegment': c.logisticsSegment,
        'transitFrom': c.transitFrom,
        'transitTo': c.transitTo,
        'neighbourhood': c.neighbourhood,
        'pincode': c.pincode,
        'surveyAddress': c.surveyAddress,
        'surveyZone': c.surveyZone,
        'surveyPincode': c.surveyPincode,
        'survey_lat': c.surveyLat,
        'survey_long': c.surveyLng,
        'notes': c.notes,
        'dataConsent': c.dataConsent.name,
      };

  // ── Helpers ─────────────────────────────────────────────────────────────────

  String _joinName(dynamic first, dynamic last) {
    final f = (first as String?) ?? '';
    final l = (last as String?) ?? '';
    final joined = [f, l].where((s) => s.isNotEmpty).join(' ');
    return joined.isEmpty ? '—' : joined;
  }

  BusinessType _parseBusinessType(String? s) {
    if (s == null) return BusinessType.retail;
    return BusinessType.values.firstWhere(
      (b) => b.name == s,
      orElse: () => BusinessType.retail,
    );
  }

  ClientStatus _parseStatus(String? s) {
    if (s == null) return ClientStatus.newClient;
    return ClientStatus.values.firstWhere(
      (st) => st.name == s,
      orElse: () => ClientStatus.newClient,
    );
  }

  DataConsent _parseConsent(String? s) {
    if (s == null) return DataConsent.full;
    return DataConsent.values.firstWhere(
      (d) => d.name == s,
      orElse: () => DataConsent.full,
    );
  }
}
