import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:uuid/uuid.dart';

import '../../domain/entities/restaurant.dart';
import '../../data/datasources/remote/naver_map_parser.dart';
import '../providers/di_providers.dart';
import '../providers/restaurant_provider.dart';
import '../providers/location_provider.dart';

/// 식당 추가 화면의 상태 모델
class AddRestaurantState {
  final bool isLoading;
  final bool isSearching;
  final String? errorMessage;
  final Restaurant? fetchedRestaurantData;
  final RestaurantFormData formData;
  final List<Restaurant> searchResults;
  final String geocodingStatus;

  const AddRestaurantState({
    this.isLoading = false,
    this.isSearching = false,
    this.errorMessage,
    this.fetchedRestaurantData,
    required this.formData,
    this.searchResults = const [],
    this.geocodingStatus = '',
  });

  AddRestaurantState copyWith({
    bool? isLoading,
    bool? isSearching,
    String? errorMessage,
    Restaurant? fetchedRestaurantData,
    RestaurantFormData? formData,
    List<Restaurant>? searchResults,
    bool clearFetchedRestaurant = false,
    bool clearError = false,
    String? geocodingStatus,
  }) {
    return AddRestaurantState(
      isLoading: isLoading ?? this.isLoading,
      isSearching: isSearching ?? this.isSearching,
      errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
      fetchedRestaurantData: clearFetchedRestaurant
          ? null
          : (fetchedRestaurantData ?? this.fetchedRestaurantData),
      formData: formData ?? this.formData,
      searchResults: searchResults ?? this.searchResults,
      geocodingStatus: geocodingStatus ?? this.geocodingStatus,
    );
  }
}

/// 식당 폼 데이터 모델
class RestaurantFormData {
  final String name;
  final String category;
  final String subCategory;
  final String description;
  final String phoneNumber;
  final String roadAddress;
  final String jibunAddress;
  final String latitude;
  final String longitude;
  final String naverUrl;

  const RestaurantFormData({
    this.name = '',
    this.category = '',
    this.subCategory = '',
    this.description = '',
    this.phoneNumber = '',
    this.roadAddress = '',
    this.jibunAddress = '',
    this.latitude = '',
    this.longitude = '',
    this.naverUrl = '',
  });

  RestaurantFormData copyWith({
    String? name,
    String? category,
    String? subCategory,
    String? description,
    String? phoneNumber,
    String? roadAddress,
    String? jibunAddress,
    String? latitude,
    String? longitude,
    String? naverUrl,
  }) {
    return RestaurantFormData(
      name: name ?? this.name,
      category: category ?? this.category,
      subCategory: subCategory ?? this.subCategory,
      description: description ?? this.description,
      phoneNumber: phoneNumber ?? this.phoneNumber,
      roadAddress: roadAddress ?? this.roadAddress,
      jibunAddress: jibunAddress ?? this.jibunAddress,
      latitude: latitude ?? this.latitude,
      longitude: longitude ?? this.longitude,
      naverUrl: naverUrl ?? this.naverUrl,
    );
  }

  /// TextEditingController로부터 폼 데이터 생성
  factory RestaurantFormData.fromControllers({
    required TextEditingController nameController,
    required TextEditingController categoryController,
    required TextEditingController subCategoryController,
    required TextEditingController descriptionController,
    required TextEditingController phoneController,
    required TextEditingController roadAddressController,
    required TextEditingController jibunAddressController,
    required TextEditingController latitudeController,
    required TextEditingController longitudeController,
    required TextEditingController naverUrlController,
  }) {
    return RestaurantFormData(
      name: nameController.text.trim(),
      category: categoryController.text.trim(),
      subCategory: subCategoryController.text.trim(),
      description: descriptionController.text.trim(),
      phoneNumber: phoneController.text.trim(),
      roadAddress: roadAddressController.text.trim(),
      jibunAddress: jibunAddressController.text.trim(),
      latitude: latitudeController.text.trim(),
      longitude: longitudeController.text.trim(),
      naverUrl: naverUrlController.text.trim(),
    );
  }

  /// Restaurant 엔티티로부터 폼 데이터 생성
  factory RestaurantFormData.fromRestaurant(Restaurant restaurant) {
    return RestaurantFormData(
      name: restaurant.name,
      category: restaurant.category,
      subCategory: restaurant.subCategory,
      description: restaurant.description ?? '',
      phoneNumber: restaurant.phoneNumber ?? '',
      roadAddress: restaurant.roadAddress,
      jibunAddress: restaurant.jibunAddress,
      latitude: restaurant.latitude.toString(),
      longitude: restaurant.longitude.toString(),
      naverUrl: restaurant.naverUrl ?? '',
    );
  }

  /// Restaurant 엔티티로 변환
  Restaurant toRestaurant() {
    final uuid = const Uuid();
    return Restaurant(
      id: uuid.v4(),
      name: name,
      category: category,
      subCategory: subCategory.isEmpty ? category : subCategory,
      description: description.isEmpty ? null : description,
      phoneNumber: phoneNumber.isEmpty ? null : phoneNumber,
      roadAddress: roadAddress,
      jibunAddress: jibunAddress.isEmpty ? roadAddress : jibunAddress,
      latitude: double.tryParse(latitude) ?? 37.5665,
      longitude: double.tryParse(longitude) ?? 126.9780,
      naverUrl: naverUrl.isEmpty ? null : naverUrl,
      source: naverUrl.isNotEmpty ? DataSource.NAVER : DataSource.USER_INPUT,
      createdAt: DateTime.now(),
      updatedAt: DateTime.now(),
    );
  }
}

/// 식당 추가 화면의 ViewModel
class AddRestaurantViewModel extends StateNotifier<AddRestaurantState> {
  final Ref _ref;

  AddRestaurantViewModel(this._ref)
    : super(const AddRestaurantState(formData: RestaurantFormData()));

  /// 상태 초기화
  void reset() {
    state = const AddRestaurantState(formData: RestaurantFormData());
  }

  /// 네이버 URL로부터 식당 정보 가져오기
  Future<void> fetchFromNaverUrl(String url) async {
    final parsed = _parseSharedNaverContent(url);

    if (parsed.url.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'URL을 입력해주세요.');
      return;
    }

    // 공유 텍스트에 포함된 상호명/도로명주소를 미리 채워 넣는다.
    state = state.copyWith(isLoading: true, clearError: true);

    try {
      final normalizedUrl = _normalizeUrl(parsed.url);
      state = state.copyWith(
        geocodingStatus: '지오코딩 시도: ${parsed.roadAddress ?? ''}',
      );
      final coords = await _tryGeocode(parsed.roadAddress ?? '');
      if (coords != null) {
        state = state.copyWith(
          geocodingStatus:
              '지오코딩 성공: ${coords.latitude.toStringAsFixed(6)}, ${coords.longitude.toStringAsFixed(6)}',
        );
      } else {
        state = state.copyWith(
          geocodingStatus: '지오코딩 실패: 현재 위치/기본 좌표를 사용할 수 있습니다.',
        );
      }
      final newForm = state.formData.copyWith(
        name: parsed.name ?? state.formData.name,
        roadAddress: parsed.roadAddress ?? state.formData.roadAddress,
        jibunAddress: state.formData.jibunAddress,
        latitude: coords != null
            ? coords.latitude.toString()
            : state.formData.latitude,
        longitude: coords != null
            ? coords.longitude.toString()
            : state.formData.longitude,
        category: '기타',
        subCategory: '기타',
        naverUrl: normalizedUrl,
      );

      state = state.copyWith(
        isLoading: false,
        fetchedRestaurantData: newForm.toRestaurant(),
        formData: newForm,
      );
    } catch (e) {
      final message = e is NaverMapParseException
          ? '네이버 지도 파싱 실패: ${e.message}'
          : e.toString();
      state = state.copyWith(
        isLoading: false,
        errorMessage: message,
        geocodingStatus: '지오코딩 실패: ${parsed.roadAddress ?? '주소 없음'}',
      );
    }
  }

  /// 네이버 검색으로 식당 목록 검색
  Future<void> searchRestaurants(
    String query, {
    double? latitude,
    double? longitude,
  }) async {
    if (query.trim().isEmpty) {
      state = state.copyWith(
        errorMessage: '검색어를 입력해주세요.',
        searchResults: const [],
      );
      return;
    }

    state = state.copyWith(isSearching: true, clearError: true);

    try {
      final repository = _ref.read(restaurantRepositoryProvider);
      final results = await repository.searchRestaurantsFromNaver(
        query: query,
        latitude: latitude,
        longitude: longitude,
      );
      state = state.copyWith(isSearching: false, searchResults: results);
    } catch (e) {
      state = state.copyWith(isSearching: false, errorMessage: e.toString());
    }
  }

  /// 검색 결과 선택
  void selectSearchResult(Restaurant restaurant) {
    state = state.copyWith(
      fetchedRestaurantData: restaurant,
      formData: RestaurantFormData.fromRestaurant(restaurant),
      clearError: true,
    );
  }

  /// 식당 정보 저장
  Future<bool> saveRestaurant() async {
    final notifier = _ref.read(restaurantNotifierProvider.notifier);
    final fallbackCategory = state.formData.category.isNotEmpty
        ? state.formData.category
        : '기타';
    final fallbackSubCategory = state.formData.subCategory.isNotEmpty
        ? state.formData.subCategory
        : fallbackCategory;

    try {
      state = state.copyWith(isLoading: true, clearError: true);
      Restaurant restaurantToSave;

      // 네이버에서 가져온 데이터가 있으면 업데이트
      final fetchedData = state.fetchedRestaurantData;
      if (fetchedData != null) {
        final coords = await _resolveCoordinates(
          latitudeText: state.formData.latitude,
          longitudeText: state.formData.longitude,
          roadAddress: state.formData.roadAddress,
          jibunAddress: state.formData.jibunAddress,
          fallbackLatitude: fetchedData.latitude,
          fallbackLongitude: fetchedData.longitude,
        );

        restaurantToSave = fetchedData.copyWith(
          name: state.formData.name,
          category: fallbackCategory,
          subCategory: fallbackSubCategory,
          description: state.formData.description.isEmpty
              ? null
              : state.formData.description,
          phoneNumber: state.formData.phoneNumber.isEmpty
              ? null
              : state.formData.phoneNumber,
          roadAddress: state.formData.roadAddress,
          jibunAddress: state.formData.jibunAddress.isEmpty
              ? state.formData.roadAddress
              : state.formData.jibunAddress,
          latitude: coords.latitude,
          longitude: coords.longitude,
          naverUrl: state.formData.naverUrl.isEmpty
              ? null
              : state.formData.naverUrl,
          updatedAt: DateTime.now(),
          needsAddressVerification: coords.usedCurrentLocation,
        );
      } else {
        // 직접 입력한 경우
        final coords = await _resolveCoordinates(
          latitudeText: state.formData.latitude,
          longitudeText: state.formData.longitude,
          roadAddress: state.formData.roadAddress,
          jibunAddress: state.formData.jibunAddress,
        );

        restaurantToSave = state.formData.toRestaurant().copyWith(
          category: fallbackCategory,
          subCategory: fallbackSubCategory,
          latitude: coords.latitude,
          longitude: coords.longitude,
          needsAddressVerification: coords.usedCurrentLocation,
        );
      }

      await notifier.addRestaurantDirect(restaurantToSave);
      state = state.copyWith(isLoading: false);
      return true;
    } catch (e) {
      state = state.copyWith(isLoading: false, errorMessage: e.toString());
      return false;
    }
  }

  /// 폼 데이터 업데이트
  void updateFormData(RestaurantFormData formData) {
    state = state.copyWith(formData: formData);
  }

  /// 에러 메시지 초기화
  void clearError() {
    state = state.copyWith(clearError: true);
  }

  /// 네이버 지도 공유 텍스트에서 URL/상호명/도로명주소를 추출한다.
  _ParsedNaverShare _parseSharedNaverContent(String raw) {
    final normalized = raw.replaceAll('\r\n', '\n').trim();

    // URL 추출
    final urlRegex = RegExp(
      r'(https?://(?:map\.naver\.com|naver\.me)[^\s]+)',
      caseSensitive: false,
    );
    final urlMatch = urlRegex.firstMatch(normalized);
    final url = urlMatch?.group(0) ?? normalized;

    // 패턴: [네이버지도]\n상호명\n도로명주소\nURL
    final lines = normalized.split('\n').map((e) => e.trim()).toList();
    String? name;
    String? roadAddress;
    if (lines.length >= 4 && lines.first.contains('네이버지도')) {
      name = lines[1].isNotEmpty ? lines[1] : null;
      roadAddress = lines[2].isNotEmpty ? lines[2] : null;
    } else {
      // 줄바꿈이 없거나 공백만 있는 경우: URL 앞 부분에서 이름/주소를 분리
      final prefix = normalized.substring(0, urlMatch?.start ?? 0).trim();
      if (prefix.isNotEmpty) {
        final cleaned = prefix.replaceFirst('[네이버지도]', '').trim();
        // 주소 패턴(시/도/구/로/길 등)을 먼저 찾는다.
        final addressRegex = RegExp(
          r'(서울|부산|대구|인천|광주|대전|울산|세종|제주|경기|강원|충북|충남|전북|전남|경북|경남)[^\n]*',
        );
        final addrMatch = addressRegex.firstMatch(cleaned);
        if (addrMatch != null) {
          roadAddress = addrMatch.group(0)?.trim();
          final extractedName = cleaned.substring(0, addrMatch.start).trim();
          name = extractedName.isNotEmpty ? extractedName : null;
        } else {
          // 주소 패턴이 없으면 첫 단어가 아닌 전체를 이름으로 유지해 공백이 있어도 깨지지 않게 함
          name = cleaned.isNotEmpty ? cleaned : null;
        }
      }
    }

    return _ParsedNaverShare(url: url, name: name, roadAddress: roadAddress);
  }

  Future<({double latitude, double longitude})?> _tryGeocode(
    String roadAddress,
  ) async {
    if (roadAddress.isEmpty) return null;
    try {
      final geocodingService = _ref.read(geocodingServiceProvider);
      final result = await geocodingService.geocode(roadAddress);
      if (result == null) return null;
      return (latitude: result.latitude, longitude: result.longitude);
    } catch (_) {
      return null;
    }
  }

  String _normalizeUrl(String rawUrl) {
    final trimmed = rawUrl.trim();
    if (trimmed.startsWith('http')) return trimmed;
    return 'https://$trimmed';
  }

  Future<({double latitude, double longitude, bool usedCurrentLocation})>
  _resolveCoordinates({
    required String latitudeText,
    required String longitudeText,
    required String roadAddress,
    required String jibunAddress,
    double? fallbackLatitude,
    double? fallbackLongitude,
  }) async {
    final parsedLat = double.tryParse(latitudeText);
    final parsedLon = double.tryParse(longitudeText);
    if (parsedLat != null && parsedLon != null) {
      state = state.copyWith(
        geocodingStatus:
            '사용자 입력 좌표 사용: ${parsedLat.toStringAsFixed(6)}, ${parsedLon.toStringAsFixed(6)}',
      );
      return (
        latitude: parsedLat,
        longitude: parsedLon,
        usedCurrentLocation: false,
      );
    }

    final geocodingService = _ref.read(geocodingServiceProvider);
    final address = roadAddress.isNotEmpty ? roadAddress : jibunAddress;
    if (address.isNotEmpty) {
      state = state.copyWith(geocodingStatus: '지오코딩 시도: $address');
      final result = await geocodingService.geocode(address);
      if (result != null) {
        state = state.copyWith(
          geocodingStatus:
              '지오코딩 성공: ${result.latitude.toStringAsFixed(6)}, ${result.longitude.toStringAsFixed(6)}',
        );
        return (
          latitude: result.latitude,
          longitude: result.longitude,
          usedCurrentLocation: false,
        );
      } else {
        state = state.copyWith(
          geocodingStatus: '지오코딩 실패: $address, 현재 위치/기본 좌표로 대체',
        );
      }
    }

    // 주소로 좌표를 얻지 못하면 현재 위치를 활용한다.
    try {
      final position = await _ref.read(currentLocationProvider.future);
      if (position != null) {
        state = state.copyWith(
          geocodingStatus:
              '현재 위치 사용: ${position.latitude.toStringAsFixed(6)}, ${position.longitude.toStringAsFixed(6)}',
        );
        return (
          latitude: position.latitude,
          longitude: position.longitude,
          usedCurrentLocation: true,
        );
      }
    } catch (_) {
      // 위치 권한 거부/오류 시 fallback 사용
    }

    if (fallbackLatitude != null && fallbackLongitude != null) {
      state = state.copyWith(
        geocodingStatus:
            '네이버 데이터 좌표 사용: ${fallbackLatitude.toStringAsFixed(6)}, ${fallbackLongitude.toStringAsFixed(6)}',
      );
      return (
        latitude: fallbackLatitude,
        longitude: fallbackLongitude,
        usedCurrentLocation: false,
      );
    }

    final defaultCoords = geocodingService.defaultCoordinates();
    state = state.copyWith(
      geocodingStatus:
          '기본 좌표 사용: ${defaultCoords.latitude.toStringAsFixed(6)}, ${defaultCoords.longitude.toStringAsFixed(6)}',
    );
    return (
      latitude: defaultCoords.latitude,
      longitude: defaultCoords.longitude,
      usedCurrentLocation: true,
    );
  }
}

class _ParsedNaverShare {
  final String url;
  final String? name;
  final String? roadAddress;

  _ParsedNaverShare({required this.url, this.name, this.roadAddress});
}

/// AddRestaurantViewModel Provider
final addRestaurantViewModelProvider =
    StateNotifierProvider.autoDispose<
      AddRestaurantViewModel,
      AddRestaurantState
    >((ref) => AddRestaurantViewModel(ref));
