import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';

import '../../core/constants/api_keys.dart';
import '../../core/network/network_client.dart';
import '../../core/network/network_config.dart';
import '../../core/errors/network_exceptions.dart';
import '../../domain/entities/restaurant.dart';
import '../datasources/remote/naver_html_extractor.dart';

/// 네이버 API 클라이언트
///
/// 네이버 오픈 API와 지도 서비스를 위한 통합 클라이언트입니다.
class NaverApiClient {
  final NetworkClient _networkClient;

  NaverApiClient({NetworkClient? networkClient})
    : _networkClient = networkClient ?? NetworkClient();

  /// 네이버 로컬 검색 API 호출
  ///
  /// 검색어와 좌표를 기반으로 주변 식당을 검색합니다.
  Future<List<NaverLocalSearchResult>> searchLocal({
    required String query,
    double? latitude,
    double? longitude,
    int display = 20,
    int start = 1,
    String sort = 'random', // random, comment
  }) async {
    // API 키 확인
    if (!ApiKeys.areKeysConfigured()) {
      throw ApiKeyException();
    }

    try {
      final response = await _networkClient.get<Map<String, dynamic>>(
        ApiKeys.naverLocalSearchEndpoint,
        queryParameters: {
          'query': query,
          'display': display,
          'start': start,
          'sort': sort,
          if (latitude != null && longitude != null) ...{
            'coordinate': '$longitude,$latitude', // 경도,위도 순서
          },
        },
        options: Options(
          headers: {
            'X-Naver-Client-Id': ApiKeys.naverClientId,
            'X-Naver-Client-Secret': ApiKeys.naverClientSecret,
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        final items = response.data!['items'] as List<dynamic>?;
        if (items == null || items.isEmpty) {
          return [];
        }

        return items
            .map(
              (item) =>
                  NaverLocalSearchResult.fromJson(item as Map<String, dynamic>),
            )
            .toList();
      }

      throw ParseException(message: '검색 결과를 파싱할 수 없습니다');
    } on DioException catch (e) {
      // 에러는 NetworkClient에서 이미 변환됨
      throw e.error ??
          ServerException(message: '네이버 API 호출 실패', statusCode: 500);
    }
  }

  /// 네이버 단축 URL 리다이렉션 처리
  ///
  /// naver.me 단축 URL을 실제 지도 URL로 변환합니다.
  Future<String> resolveShortUrl(String shortUrl) async {
    if (!shortUrl.contains('naver.me')) {
      debugPrint('NaverApiClient: 단축 URL이 아님, 원본 반환 - $shortUrl');
      return shortUrl;
    }

    try {
      debugPrint('NaverApiClient: 단축 URL 리디렉션 처리 시작 - $shortUrl');

      // 웹 환경에서는 CORS 프록시 사용
      if (kIsWeb) {
        return await _resolveShortUrlViaProxy(shortUrl);
      }

      // 모바일 환경에서는 여러 단계의 리다이렉션 처리
      String currentUrl = shortUrl;
      int redirectCount = 0;
      const maxRedirects = 10;

      while (redirectCount < maxRedirects) {
        debugPrint(
          'NaverApiClient: 리다이렉션 시도 #${redirectCount + 1} - $currentUrl',
        );

        final response = await _networkClient.get(
          currentUrl,
          options: Options(
            followRedirects: false,
            validateStatus: (status) => true, // 모든 상태 코드 허용
            headers: {'User-Agent': NetworkConfig.userAgent},
          ),
          useCache: false,
        );

        debugPrint('NaverApiClient: 응답 상태 코드 - ${response.statusCode}');

        // 리다이렉션 체크 (301, 302, 307, 308)
        if ([301, 302, 307, 308].contains(response.statusCode)) {
          final location = response.headers['location']?.firstOrNull;
          if (location != null) {
            debugPrint('NaverApiClient: Location 헤더 발견 - $location');

            // 상대 경로인 경우 절대 경로로 변환
            if (!location.startsWith('http')) {
              final Uri baseUri = Uri.parse(currentUrl);
              currentUrl = baseUri.resolve(location).toString();
            } else {
              currentUrl = location;
            }

            // 목표 URL에 도달했는지 확인
            if (currentUrl.contains('pcmap.place.naver.com') ||
                currentUrl.contains('map.naver.com/p/')) {
              debugPrint('NaverApiClient: 최종 URL 도착 - $currentUrl');
              return currentUrl;
            }

            redirectCount++;
          } else {
            debugPrint('NaverApiClient: Location 헤더 없음');
            break;
          }
        } else if (response.statusCode == 200) {
          // 200 OK인 경우 meta refresh 태그 확인
          debugPrint('NaverApiClient: 200 OK - meta refresh 태그 확인');

          final String? html = response.data as String?;
          if (html != null &&
              html.contains('meta') &&
              html.contains('refresh')) {
            final metaRefreshRegex = RegExp(
              '<meta[^>]+http-equiv=["\']refresh["\'][^>]+content=["\']\\d+;\\s*url=([^"\'>]+)',
              caseSensitive: false,
            );

            final match = metaRefreshRegex.firstMatch(html);
            if (match != null) {
              final redirectUrl = match.group(1)!;
              debugPrint('NaverApiClient: Meta refresh URL 발견 - $redirectUrl');

              // 상대 경로 처리
              if (!redirectUrl.startsWith('http')) {
                final Uri baseUri = Uri.parse(currentUrl);
                currentUrl = baseUri.resolve(redirectUrl).toString();
              } else {
                currentUrl = redirectUrl;
              }

              redirectCount++;
              continue;
            }
          }

          // meta refresh가 없으면 현재 URL이 최종 URL
          debugPrint('NaverApiClient: 200 OK - 최종 URL - $currentUrl');
          return currentUrl;
        } else {
          debugPrint('NaverApiClient: 리다이렉션 아님 - 상태 코드 ${response.statusCode}');
          break;
        }
      }

      // 모든 시도 후 현재 URL 반환
      debugPrint('NaverApiClient: 최종 URL - $currentUrl');
      return currentUrl;
    } catch (e) {
      debugPrint('NaverApiClient: 단축 URL 리다이렉션 실패 - $e');
      return shortUrl;
    }
  }

  /// 프록시를 통한 단축 URL 리다이렉션 (웹 환경)
  Future<String> _resolveShortUrlViaProxy(String shortUrl) async {
    try {
      final proxyUrl =
          '${NetworkConfig.corsProxyUrl}?url=${Uri.encodeComponent(shortUrl)}';

      final response = await _networkClient.get<Map<String, dynamic>>(
        proxyUrl,
        options: Options(headers: {'Accept': 'application/json'}),
        useCache: false,
      );

      if (response.statusCode == 200 && response.data != null) {
        final data = response.data!;

        // status.url 확인
        if (data['status'] != null &&
            data['status'] is Map &&
            data['status']['url'] != null) {
          final finalUrl = data['status']['url'] as String;
          debugPrint('NaverApiClient: 프록시 최종 URL - $finalUrl');
          return finalUrl;
        }

        // contents에서 meta refresh 태그 찾기
        final contents = data['contents'] as String?;
        if (contents != null && contents.isNotEmpty) {
          final metaRefreshRegex = RegExp(
            '<meta\\s+http-equiv=["\']refresh["\']'
            '\\s+content=["\']0;\\s*url=([^"\']+)["\']',
            caseSensitive: false,
          );

          final match = metaRefreshRegex.firstMatch(contents);
          if (match != null) {
            final redirectUrl = match.group(1)!;
            debugPrint('NaverApiClient: Meta refresh URL - $redirectUrl');
            return redirectUrl;
          }
        }
      }

      return shortUrl;
    } catch (e) {
      debugPrint('NaverApiClient: 프록시 리다이렉션 실패 - $e');
      return shortUrl;
    }
  }

  /// 네이버 지도 HTML 가져오기
  ///
  /// 웹 환경에서는 CORS 프록시를 사용합니다.
  Future<String> fetchMapPageHtml(String url) async {
    try {
      if (kIsWeb) {
        return await _fetchViaProxy(url);
      }

      // 모바일 환경에서는 직접 요청
      final response = await _networkClient.get<String>(
        url,
        options: Options(
          responseType: ResponseType.plain,
          headers: {
            'User-Agent': NetworkConfig.userAgent,
            'Referer': 'https://map.naver.com',
          },
        ),
        useCache: false, // 네이버 지도는 동적 콘텐츠이므로 캐시 사용 안함
      );

      if (response.statusCode == 200 && response.data != null) {
        return response.data!;
      }

      throw ServerException(
        message: 'HTML을 가져올 수 없습니다',
        statusCode: response.statusCode ?? 500,
      );
    } on DioException catch (e) {
      throw e.error ??
          ServerException(message: 'HTML 가져오기 실패', statusCode: 500);
    }
  }

  /// 프록시를 통한 HTML 가져오기 (웹 환경)
  Future<String> _fetchViaProxy(String url) async {
    final proxyUrl =
        '${NetworkConfig.corsProxyUrl}?url=${Uri.encodeComponent(url)}';

    final response = await _networkClient.get<Map<String, dynamic>>(
      proxyUrl,
      options: Options(headers: {'Accept': 'application/json'}),
    );

    if (response.statusCode == 200 && response.data != null) {
      final data = response.data!;

      // 상태 코드 확인
      if (data['status'] != null && data['status'] is Map) {
        final statusMap = data['status'] as Map<String, dynamic>;
        final httpCode = statusMap['http_code'];
        if (httpCode != null && httpCode != 200) {
          throw ServerException(
            message: '네이버 서버 응답 오류',
            statusCode: httpCode as int,
          );
        }
      }

      // contents 반환
      final contents = data['contents'];
      if (contents == null || contents.toString().isEmpty) {
        throw ParseException(message: '빈 응답을 받았습니다');
      }

      return contents.toString();
    }

    throw ServerException(
      message: '프록시 요청 실패',
      statusCode: response.statusCode ?? 500,
    );
  }

  /// GraphQL 쿼리 실행
  ///
  /// 네이버 지도 API의 GraphQL 엔드포인트에 요청을 보냅니다.
  Future<Map<String, dynamic>> fetchGraphQL({
    required String operationName,
    required Map<String, dynamic> variables,
    required String query,
  }) async {
    const String graphqlUrl = 'https://pcmap-api.place.naver.com/graphql';

    try {
      final response = await _networkClient.post<Map<String, dynamic>>(
        graphqlUrl,
        data: {
          'operationName': operationName,
          'variables': variables,
          'query': query,
        },
        options: Options(
          headers: {
            'Content-Type': 'application/json',
            'Referer': 'https://map.naver.com/',
            'User-Agent': NetworkConfig.userAgent,
          },
        ),
      );

      if (response.statusCode == 200 && response.data != null) {
        return response.data!;
      }

      throw ParseException(message: 'GraphQL 응답을 파싱할 수 없습니다');
    } on DioException catch (e) {
      throw e.error ??
          ServerException(message: 'GraphQL 요청 실패', statusCode: 500);
    }
  }

  /// pcmap URL에서 한글 텍스트 리스트 가져오기
  ///
  /// restaurant/{ID}/home 형식의 URL에서 모든 한글 텍스트를 추출합니다.
  Future<Map<String, dynamic>> fetchKoreanTextsFromPcmap(String placeId) async {
    // restaurant 타입 URL 사용
    final pcmapUrl = 'https://pcmap.place.naver.com/restaurant/$placeId/home';

    try {
      debugPrint('========== 네이버 pcmap 한글 추출 시작 ==========');
      debugPrint('요청 URL: $pcmapUrl');
      debugPrint('Place ID: $placeId');

      String html;
      if (kIsWeb) {
        // 웹 환경에서는 프록시 사용
        html = await _fetchViaProxy(pcmapUrl);
      } else {
        // 모바일 환경에서는 직접 요청
        final response = await _networkClient.get<String>(
          pcmapUrl,
          options: Options(
            responseType: ResponseType.plain,
            headers: {
              'User-Agent': NetworkConfig.userAgent,
              'Accept': 'text/html,application/xhtml+xml',
              'Accept-Language': 'ko-KR,ko;q=0.9,en;q=0.8',
              'Referer': 'https://map.naver.com/',
            },
          ),
          useCache: false,
        );

        if (response.statusCode != 200 || response.data == null) {
          debugPrint(
            'NaverApiClient: pcmap 페이지 로드 실패 - status: ${response.statusCode}',
          );
          return {
            'success': false,
            'error': 'HTTP ${response.statusCode}',
            'koreanTexts': <String>[],
          };
        }

        html = response.data!;
      }

      // 모든 한글 텍스트 추출
      final koreanTexts = NaverHtmlExtractor.extractAllValidKoreanTexts(html);
      
      // JSON-LD 데이터 추출 시도
      final jsonLdName = NaverHtmlExtractor.extractPlaceNameFromJsonLd(html);
      
      // Apollo State 데이터 추출 시도
      final apolloName = NaverHtmlExtractor.extractPlaceNameFromApolloState(html);

      debugPrint('========== 추출 결과 ==========');
      debugPrint('총 한글 텍스트 수: ${koreanTexts.length}');
      debugPrint('JSON-LD 상호명: $jsonLdName');
      debugPrint('Apollo State 상호명: $apolloName');
      debugPrint('=====================================');

      return {
        'success': true,
        'placeId': placeId,
        'url': pcmapUrl,
        'koreanTexts': koreanTexts,
        'jsonLdName': jsonLdName,
        'apolloStateName': apolloName,
        'extractedAt': DateTime.now().toIso8601String(),
      };
    } catch (e) {
      debugPrint('NaverApiClient: pcmap 페이지 파싱 실패 - $e');
      return {
        'success': false,
        'error': e.toString(),
        'koreanTexts': <String>[],
      };
    }
  }


  /// 최종 리디렉션 URL 획득
  ///
  /// 주어진 URL이 리디렉션되는 최종 URL을 반환합니다.
  Future<String> getFinalRedirectUrl(String url) async {
    try {
      debugPrint('NaverApiClient: 최종 리디렉션 URL 획득 중 - $url');

      // 429 에러 방지를 위한 지연
      await Future.delayed(const Duration(milliseconds: 500));

      final response = await _networkClient.get(
        url,
        options: Options(
          followRedirects: true,
          maxRedirects: 5,
          responseType: ResponseType.plain,
        ),
        useCache: false,
      );

      final finalUrl = response.realUri.toString();
      debugPrint('NaverApiClient: 최종 리디렉션 URL - $finalUrl');

      return finalUrl;
    } catch (e) {
      debugPrint('NaverApiClient: 최종 리디렉션 URL 획득 실패 - $e');
      return url;
    }
  }




  /// 리소스 정리
  void dispose() {
    _networkClient.dispose();
  }
}

/// 네이버 로컬 검색 결과
class NaverLocalSearchResult {
  final String title;
  final String link;
  final String category;
  final String description;
  final String telephone;
  final String address;
  final String roadAddress;
  final int mapx; // 경도 (x좌표)
  final int mapy; // 위도 (y좌표)

  NaverLocalSearchResult({
    required this.title,
    required this.link,
    required this.category,
    required this.description,
    required this.telephone,
    required this.address,
    required this.roadAddress,
    required this.mapx,
    required this.mapy,
  });

  factory NaverLocalSearchResult.fromJson(Map<String, dynamic> json) {
    return NaverLocalSearchResult(
      title: _removeHtmlTags(json['title'] ?? ''),
      link: json['link'] ?? '',
      category: json['category'] ?? '',
      description: _removeHtmlTags(json['description'] ?? ''),
      telephone: json['telephone'] ?? '',
      address: json['address'] ?? '',
      roadAddress: json['roadAddress'] ?? '',
      mapx: int.tryParse(json['mapx']?.toString() ?? '0') ?? 0,
      mapy: int.tryParse(json['mapy']?.toString() ?? '0') ?? 0,
    );
  }

  /// HTML 태그 제거
  static String _removeHtmlTags(String text) {
    return text.replaceAll(RegExp(r'<[^>]+>'), '');
  }

  /// 위도 (십진도)
  double get latitude => mapy / 10000000.0;

  /// 경도 (십진도)
  double get longitude => mapx / 10000000.0;

  /// Restaurant 엔티티로 변환
  Restaurant toRestaurant({required String id}) {
    // 카테고리 파싱
    final categories = category.split('>').map((c) => c.trim()).toList();
    final mainCategory = categories.isNotEmpty ? categories.first : '기타';
    final subCategory = categories.length > 1 ? categories.last : mainCategory;

    return Restaurant(
      id: id,
      name: title,
      category: mainCategory,
      subCategory: subCategory,
      description: description.isNotEmpty ? description : null,
      phoneNumber: telephone.isNotEmpty ? telephone : null,
      roadAddress: roadAddress.isNotEmpty ? roadAddress : address,
      jibunAddress: address,
      latitude: latitude,
      longitude: longitude,
      lastVisitDate: null,
      source: DataSource.NAVER,
      createdAt: DateTime.now(),
      updatedAt: DateTime.now(),
      naverPlaceId: null,
      naverUrl: link.isNotEmpty ? link : null,
      businessHours: null,
      lastVisited: null,
      visitCount: 0,
    );
  }
}
