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

import '../../domain/entities/restaurant.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;

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

  AddRestaurantState copyWith({
    bool? isLoading,
    bool? isSearching,
    String? errorMessage,
    Restaurant? fetchedRestaurantData,
    RestaurantFormData? formData,
    List<Restaurant>? searchResults,
    bool clearFetchedRestaurant = false,
    bool clearError = false,
  }) {
    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,
    );
  }
}

/// 식당 폼 데이터 모델
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 {
    if (url.trim().isEmpty) {
      state = state.copyWith(errorMessage: 'URL을 입력해주세요.');
      return;
    }

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

    try {
      final repository = _ref.read(restaurantRepositoryProvider);
      final restaurant = await repository.previewRestaurantFromUrl(url);

      state = state.copyWith(
        isLoading: false,
        fetchedRestaurantData: restaurant,
        formData: RestaurantFormData.fromRestaurant(restaurant),
      );
    } catch (e) {
      state = state.copyWith(isLoading: false, errorMessage: e.toString());
    }
  }

  /// 네이버 검색으로 식당 목록 검색
  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);

    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: state.formData.category,
          subCategory: state.formData.subCategory.isEmpty
              ? state.formData.category
              : state.formData.subCategory,
          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(
          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);
  }

  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) {
      return (
        latitude: parsedLat,
        longitude: parsedLon,
        usedCurrentLocation: false,
      );
    }

    final geocodingService = _ref.read(geocodingServiceProvider);
    final address = roadAddress.isNotEmpty ? roadAddress : jibunAddress;
    if (address.isNotEmpty) {
      final result = await geocodingService.geocode(address);
      if (result != null) {
        return (
          latitude: result.latitude,
          longitude: result.longitude,
          usedCurrentLocation: false,
        );
      }
    }

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

    if (fallbackLatitude != null && fallbackLongitude != null) {
      return (
        latitude: fallbackLatitude,
        longitude: fallbackLongitude,
        usedCurrentLocation: false,
      );
    }

    final defaultCoords = geocodingService.defaultCoordinates();
    return (
      latitude: defaultCoords.latitude,
      longitude: defaultCoords.longitude,
      usedCurrentLocation: true,
    );
  }
}

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