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

import '../config/api_config.dart';
import '../utils/web_storage.dart';

abstract class PublicHttpClient {
  Future<Response<dynamic>> get(String path, {Map<String, dynamic>? headers});
  Future<Response<dynamic>> post(String path, {dynamic data, Map<String, dynamic>? headers});
  Future<Response<dynamic>> put(String path, {dynamic data, Map<String, dynamic>? headers});
}

class DioPublicHttpClient implements PublicHttpClient {
  final Dio _dio;
  DioPublicHttpClient({Dio? dio}) : _dio = dio ?? Dio(BaseOptions(
    baseUrl: ApiConfig.baseUrl,
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 10),
    contentType: 'application/json',
  ));

  @override
  Future<Response> get(String path, {Map<String, dynamic>? headers}) {
    return _dio.get(path, options: Options(headers: headers));
  }

  @override
  Future<Response> post(String path, {data, Map<String, dynamic>? headers}) {
    return _dio.post(path, data: data ?? {}, options: Options(headers: headers));
  }

  @override
  Future<Response> put(String path, {data, Map<String, dynamic>? headers}) {
    return _dio.put(path, data: data ?? {}, options: Options(headers: headers));
  }
}

class PublicEventService {
  final PublicHttpClient _http;
  PublicEventService() : _http = DioPublicHttpClient();
  @visibleForTesting
  PublicEventService.forTest(this._http);

  static String tokenKey(String publicHash) => 'event_token_' + publicHash;

  String? getSavedToken(String publicHash) {
    return WebStorage.getItem(tokenKey(publicHash));
  }

  void saveToken(String publicHash, String token) {
    WebStorage.setItem(tokenKey(publicHash), token);
  }

  void removeToken(String publicHash) {
    WebStorage.removeItem(tokenKey(publicHash));
  }

  Map<String, dynamic>? _authHeader(String? token) {
    if (token == null || token.isEmpty) return null;
    return { 'Authorization': 'Bearer ' + token };
  }

  Future<Map<String, dynamic>> fetchEvent(String publicHash) async {
    final saved = getSavedToken(publicHash);
    // 백엔드는 GET이 아니라 POST('/:publicHash')로 최초 진입/토큰 인증을 처리함
    final res = await _http.post(
      '/public/' + publicHash,
      data: {},
      headers: _authHeader(saved),
    );
    final data = res.data as Map<String, dynamic>;
    // 응답에 token이 있으면 저장 (토큰 재발급 케이스)
    final token = data['token']?.toString();
    if (token != null && token.isNotEmpty) {
      saveToken(publicHash, token);
    }
    return data;
  }

  Future<Map<String, dynamic>> enter(String publicHash, {String? password}) async {
    final saved = getSavedToken(publicHash);
    try {
      // 우선 저장된 토큰으로 시도, 실패 시 비밀번호로 재시도는 서버가 처리
      final res = await _http.post(
        '/public/' + publicHash,
        data: password != null ? { 'password': password } : {},
        headers: _authHeader(saved),
      );
      final data = res.data as Map<String, dynamic>;
      // 응답에 token이 있으면 저장
      final token = data['token']?.toString();
      if (token != null && token.isNotEmpty) {
        saveToken(publicHash, token);
      }
      return data;
    } on DioException catch (e) {
      final status = e.response?.statusCode;
      if (status == 401 || status == 403) {
        removeToken(publicHash);
      }
      rethrow;
    }
  }

  Future<void> addOrUpdateParticipant(String publicHash, {
    String? participantId,
    required String memberId,
    required String status,
    required String paymentStatus,
    String? comment,
  }) async {
    final token = getSavedToken(publicHash);
    final payload = {
      if (participantId != null) 'participantId': participantId,
      'memberId': memberId,
      'status': status,
      'paymentStatus': paymentStatus,
      if (comment != null) 'comment': comment,
    };
    await _http.post(
      '/public/' + publicHash + '/participant',
      data: payload,
      headers: _authHeader(token),
    );
  }

  Future<void> addGuest(String publicHash, {
    required String name,
    String? phone,
    String? gender,
    num? average,
  }) async {
    final token = getSavedToken(publicHash);
    final payload = {
      'name': name,
      if (phone != null) 'phone': phone,
      if (gender != null) 'gender': gender,
      if (average != null) 'average': average,
    };
    await _http.post(
      '/public/' + publicHash + '/guest',
      data: payload,
      headers: _authHeader(token),
    );
  }

  Future<void> updateScore(String publicHash, {
    required String participantId,
    required int gameNumber,
    required int score,
    int? handicap,
    dynamic teamNumber,
  }) async {
    final token = getSavedToken(publicHash);
    final payload = {
      'participantId': participantId,
      'gameNumber': gameNumber,
      'score': score,
      if (handicap != null) 'handicap': handicap,
      if (teamNumber != null) 'teamNumber': teamNumber,
    };
    await _http.put(
      '/public/' + publicHash + '/score',
      data: payload,
      headers: _authHeader(token),
    );
  }

  Future<void> updateTeamHandicap(String publicHash, {
    required int teamNumber,
    required int handicap,
  }) async {
    final token = getSavedToken(publicHash);
    await _http.put(
      '/public/' + publicHash + '/team/' + teamNumber.toString() + '/handicap',
      data: { 'teamNumber': teamNumber, 'handicap': handicap },
      headers: _authHeader(token),
    );
  }
}
