// 自然写互动课堂平板端应用软件 V1.0
// service/api_service.dart - 云平台API服务（Dio HTTP客户端）

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:crypto/crypto.dart';

/// 云平台API基础路径配置
class ApiConfig {
  /// 生产环境API地址
  static const String productionBaseUrl = 'https://api.writech.com/v1';

  /// 测试环境API地址
  static const String stagingBaseUrl = 'https://staging-api.writech.com/v1';

  /// 连接超时时间（毫秒）
  static const int connectTimeout = 15000;

  /// 接收超时时间（毫秒）
  static const int receiveTimeout = 30000;

  /// Token刷新路径
  static const String refreshTokenPath = '/auth/refresh';

  /// 最大重试次数
  static const int maxRetryCount = 3;

  /// HMAC签名密钥标识
  static const String hmacKeyId = 'writech-pad-v1';
}

/// API响应数据统一封装
class ApiResponse<T> {
  final int code;
  final String message;
  final T? data;
  final String? requestId;

  ApiResponse({
    required this.code,
    required this.message,
    this.data,
    this.requestId,
  });

  /// 判断请求是否成功
  bool get isSuccess => code == 0 || code == 200;

  /// 从JSON解析响应
  factory ApiResponse.fromJson(
    Map<String, dynamic> json,
    T Function(dynamic)? fromData,
  ) {
    return ApiResponse(
      code: json['code'] ?? -1,
      message: json['message'] ?? '未知错误',
      data: json['data'] != null && fromData != null
          ? fromData(json['data'])
          : json['data'] as T?,
      requestId: json['request_id'],
    );
  }
}

/// 离线请求队列项
class OfflineRequest {
  final String id;
  final String method;
  final String path;
  final Map<String, dynamic>? data;
  final DateTime createdAt;
  int retryCount;

  OfflineRequest({
    required this.id,
    required this.method,
    required this.path,
    this.data,
    required this.createdAt,
    this.retryCount = 0,
  });

  /// 序列化为JSON用于本地持久化
  Map<String, dynamic> toJson() => {
    'id': id,
    'method': method,
    'path': path,
    'data': data,
    'created_at': createdAt.toIso8601String(),
    'retry_count': retryCount,
  };

  /// 从JSON反序列化
  factory OfflineRequest.fromJson(Map<String, dynamic> json) {
    return OfflineRequest(
      id: json['id'],
      method: json['method'],
      path: json['path'],
      data: json['data'],
      createdAt: DateTime.parse(json['created_at']),
      retryCount: json['retry_count'] ?? 0,
    );
  }
}

/// 平板端云平台API服务
/// 负责与云平台的所有HTTP通信，包括：
/// - JWT双令牌认证与自动刷新
/// - HMAC-SHA256请求签名
/// - 离线请求队列暂存
/// - 学生简化登录（班级+姓名/学号）
class PadApiService {
  late final Dio _dio;
  final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();

  /// 当前访问令牌
  String? _accessToken;

  /// 刷新令牌
  String? _refreshToken;

  /// Token刷新锁，防止并发刷新
  Completer<bool>? _refreshCompleter;

  /// 离线请求队列
  final List<OfflineRequest> _offlineQueue = [];

  /// 网络状态标志
  bool _isOnline = true;

  /// API事件流控制器（登录状态变化等）
  final StreamController<String> _eventController =
      StreamController<String>.broadcast();

  /// API事件流
  Stream<String> get eventStream => _eventController.stream;

  /// 单例实例
  static PadApiService? _instance;

  /// 获取单例
  static PadApiService get instance {
    _instance ??= PadApiService._internal();
    return _instance!;
  }

  /// 私有构造函数，初始化Dio客户端
  PadApiService._internal() {
    _dio = Dio(BaseOptions(
      baseUrl: ApiConfig.productionBaseUrl,
      connectTimeout: Duration(milliseconds: ApiConfig.connectTimeout),
      receiveTimeout: Duration(milliseconds: ApiConfig.receiveTimeout),
      headers: {
        'Content-Type': 'application/json',
        'X-Client-Platform': 'pad',
        'X-Client-Version': '1.0.0',
      },
    ));

    // 添加请求拦截器：自动附加Token和HMAC签名
    _dio.interceptors.add(InterceptorsWrapper(
      onRequest: _onRequest,
      onResponse: _onResponse,
      onError: _onError,
    ));

    // 从安全存储恢复令牌
    _restoreTokens();
  }

  /// 从安全存储恢复上次保存的令牌
  Future<void> _restoreTokens() async {
    _accessToken = await _secureStorage.read(key: 'access_token');
    _refreshToken = await _secureStorage.read(key: 'refresh_token');
  }

  /// 请求拦截器：附加Authorization头和HMAC签名
  void _onRequest(
    RequestOptions options,
    RequestInterceptorHandler handler,
  ) {
    // 附加JWT访问令牌
    if (_accessToken != null) {
      options.headers['Authorization'] = 'Bearer $_accessToken';
    }

    // 生成HMAC-SHA256请求签名
    final timestamp = DateTime.now().millisecondsSinceEpoch.toString();
    options.headers['X-Timestamp'] = timestamp;
    final signature = _generateSignature(
      options.method,
      options.path,
      timestamp,
      options.data,
    );
    options.headers['X-Signature'] = signature;

    handler.next(options);
  }

  /// 响应拦截器：统一处理响应
  void _onResponse(
    Response response,
    ResponseInterceptorHandler handler,
  ) {
    handler.next(response);
  }

  /// 错误拦截器：处理401自动刷新Token、离线暂存等
  Future<void> _onError(
    DioException error,
    ErrorInterceptorHandler handler,
  ) async {
    // 网络不可用时，将请求加入离线队列
    if (error.type == DioExceptionType.connectionError ||
        error.type == DioExceptionType.connectionTimeout) {
      _isOnline = false;
      _enqueueOfflineRequest(error.requestOptions);
      handler.reject(error);
      return;
    }

    // 401未授权：尝试刷新Token后重试
    if (error.response?.statusCode == 401) {
      final refreshSuccess = await _refreshAccessToken();
      if (refreshSuccess) {
        // Token刷新成功，使用新Token重试原请求
        final retryOptions = error.requestOptions;
        retryOptions.headers['Authorization'] = 'Bearer $_accessToken';
        try {
          final response = await _dio.fetch(retryOptions);
          handler.resolve(response);
          return;
        } catch (retryError) {
          // 重试也失败了
        }
      } else {
        // Token刷新失败，通知登出
        _eventController.add('token_expired');
      }
    }

    handler.reject(error);
  }

  /// 生成HMAC-SHA256请求签名
  /// 签名内容: METHOD\nPATH\nTIMESTAMP\nBODY_SHA256
  String _generateSignature(
    String method,
    String path,
    String timestamp,
    dynamic body,
  ) {
    // 计算请求体SHA256哈希
    String bodyHash = '';
    if (body != null) {
      final bodyStr = body is String ? body : jsonEncode(body);
      bodyHash = sha256.convert(utf8.encode(bodyStr)).toString();
    }

    // 拼接签名原文
    final signContent = '$method\n$path\n$timestamp\n$bodyHash';
    final hmacKey = utf8.encode(ApiConfig.hmacKeyId);
    final hmac = Hmac(sha256, hmacKey);
    final digest = hmac.convert(utf8.encode(signContent));

    return digest.toString();
  }

  /// 刷新访问令牌
  /// 使用Completer防止并发多次刷新
  Future<bool> _refreshAccessToken() async {
    // 如果已经在刷新中，等待结果
    if (_refreshCompleter != null) {
      return _refreshCompleter!.future;
    }

    _refreshCompleter = Completer<bool>();

    try {
      if (_refreshToken == null) {
        _refreshCompleter!.complete(false);
        return false;
      }

      // 发送刷新请求（不经过拦截器避免死循环）
      final response = await Dio().post(
        '${ApiConfig.productionBaseUrl}${ApiConfig.refreshTokenPath}',
        data: {'refresh_token': _refreshToken},
      );

      if (response.statusCode == 200 && response.data['code'] == 0) {
        _accessToken = response.data['data']['access_token'];
        _refreshToken = response.data['data']['refresh_token'];

        // 持久化新令牌到安全存储
        await _secureStorage.write(
          key: 'access_token',
          value: _accessToken,
        );
        await _secureStorage.write(
          key: 'refresh_token',
          value: _refreshToken,
        );

        _refreshCompleter!.complete(true);
        return true;
      }

      _refreshCompleter!.complete(false);
      return false;
    } catch (e) {
      _refreshCompleter!.complete(false);
      return false;
    } finally {
      _refreshCompleter = null;
    }
  }

  /// 将失败的请求加入离线队列
  void _enqueueOfflineRequest(RequestOptions options) {
    final offlineReq = OfflineRequest(
      id: DateTime.now().microsecondsSinceEpoch.toString(),
      method: options.method,
      path: options.path,
      data: options.data is Map ? options.data : null,
      createdAt: DateTime.now(),
    );
    _offlineQueue.add(offlineReq);
  }

  /// 网络恢复后，批量重发离线队列中的请求
  Future<void> flushOfflineQueue() async {
    if (_offlineQueue.isEmpty) return;

    _isOnline = true;
    final pendingRequests = List<OfflineRequest>.from(_offlineQueue);
    _offlineQueue.clear();

    for (final req in pendingRequests) {
      try {
        if (req.retryCount >= ApiConfig.maxRetryCount) continue;
        req.retryCount++;

        switch (req.method.toUpperCase()) {
          case 'POST':
            await _dio.post(req.path, data: req.data);
            break;
          case 'PUT':
            await _dio.put(req.path, data: req.data);
            break;
          case 'DELETE':
            await _dio.delete(req.path);
            break;
          default:
            await _dio.get(req.path);
        }
      } catch (e) {
        // 重发失败的请求重新加入队列
        if (req.retryCount < ApiConfig.maxRetryCount) {
          _offlineQueue.add(req);
        }
      }
    }
  }

  // ============================================================
  //  学生登录接口（简化登录，班级+姓名/学号）
  // ============================================================

  /// 学生简化登录（无需手机号，仅班级+姓名或学号）
  Future<ApiResponse<Map<String, dynamic>>> studentLogin({
    required String schoolCode,
    required String classId,
    required String studentName,
    String? studentNo,
  }) async {
    try {
      final response = await _dio.post('/auth/student/login', data: {
        'school_code': schoolCode,
        'class_id': classId,
        'student_name': studentName,
        'student_no': studentNo,
        'device_type': 'pad',
      });

      final result = ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );

      // 保存登录令牌
      if (result.isSuccess && result.data != null) {
        _accessToken = result.data!['access_token'];
        _refreshToken = result.data!['refresh_token'];
        await _secureStorage.write(
          key: 'access_token',
          value: _accessToken,
        );
        await _secureStorage.write(
          key: 'refresh_token',
          value: _refreshToken,
        );
      }

      return result;
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '网络请求失败');
    }
  }

  /// 教师登录（手机号+验证码）
  Future<ApiResponse<Map<String, dynamic>>> teacherLogin({
    required String phone,
    required String verifyCode,
  }) async {
    try {
      final response = await _dio.post('/auth/teacher/login', data: {
        'phone': phone,
        'verify_code': verifyCode,
        'device_type': 'pad',
      });

      final result = ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );

      if (result.isSuccess && result.data != null) {
        _accessToken = result.data!['access_token'];
        _refreshToken = result.data!['refresh_token'];
        await _secureStorage.write(
          key: 'access_token',
          value: _accessToken,
        );
        await _secureStorage.write(
          key: 'refresh_token',
          value: _refreshToken,
        );
      }

      return result;
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '网络请求失败');
    }
  }

  // ============================================================
  //  作业相关接口
  // ============================================================

  /// 获取学生待完成作业列表
  Future<ApiResponse<List<dynamic>>> getHomeworkList({
    int page = 1,
    int pageSize = 20,
    String? status,
  }) async {
    try {
      final response = await _dio.get('/homework/list', queryParameters: {
        'page': page,
        'page_size': pageSize,
        if (status != null) 'status': status,
      });
      return ApiResponse<List<dynamic>>.fromJson(
        response.data,
        (data) => data as List<dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取作业列表失败');
    }
  }

  /// 下载作业详情（含题目内容，支持离线作答）
  Future<ApiResponse<Map<String, dynamic>>> downloadHomework(
    String homeworkId,
  ) async {
    try {
      final response = await _dio.get('/homework/detail/$homeworkId');
      return ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '下载作业失败');
    }
  }

  /// 提交作业（含笔迹数据）
  Future<ApiResponse<Map<String, dynamic>>> submitHomework({
    required String homeworkId,
    required List<Map<String, dynamic>> strokePages,
    int? timeCostSeconds,
  }) async {
    try {
      final response = await _dio.post('/homework/submit', data: {
        'homework_id': homeworkId,
        'stroke_pages': strokePages,
        'time_cost': timeCostSeconds,
        'submit_time': DateTime.now().toIso8601String(),
      });
      return ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );
    } on DioException catch (e) {
      // 离线时暂存提交请求
      if (!_isOnline) {
        _enqueueOfflineRequest(e.requestOptions);
      }
      return ApiResponse(code: -1, message: e.message ?? '提交作业失败');
    }
  }

  /// 获取作业批改结果
  Future<ApiResponse<Map<String, dynamic>>> getHomeworkResult(
    String homeworkId,
  ) async {
    try {
      final response = await _dio.get('/homework/result/$homeworkId');
      return ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取批改结果失败');
    }
  }

  // ============================================================
  //  字帖练习接口
  // ============================================================

  /// 获取字帖模板列表（按年级/学科分类）
  Future<ApiResponse<List<dynamic>>> getCopybookTemplates({
    required String grade,
    String? subject,
    int page = 1,
  }) async {
    try {
      final response = await _dio.get('/copybook/templates', queryParameters: {
        'grade': grade,
        'subject': subject,
        'page': page,
      });
      return ApiResponse<List<dynamic>>.fromJson(
        response.data,
        (data) => data as List<dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取字帖失败');
    }
  }

  /// 上传练字笔迹评分
  Future<ApiResponse<Map<String, dynamic>>> submitPracticeStroke({
    required String templateId,
    required String character,
    required List<Map<String, dynamic>> strokes,
  }) async {
    try {
      final response = await _dio.post('/copybook/evaluate', data: {
        'template_id': templateId,
        'character': character,
        'strokes': strokes,
      });
      return ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '提交练字评分失败');
    }
  }

  // ============================================================
  //  错题本接口
  // ============================================================

  /// 获取错题列表（按知识点/科目分类）
  Future<ApiResponse<List<dynamic>>> getErrorBookList({
    String? subject,
    String? knowledgePoint,
    int page = 1,
    int pageSize = 20,
  }) async {
    try {
      final response = await _dio.get('/error-book/list', queryParameters: {
        if (subject != null) 'subject': subject,
        if (knowledgePoint != null) 'knowledge_point': knowledgePoint,
        'page': page,
        'page_size': pageSize,
      });
      return ApiResponse<List<dynamic>>.fromJson(
        response.data,
        (data) => data as List<dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取错题本失败');
    }
  }

  // ============================================================
  //  学情与学习计划接口
  // ============================================================

  /// 获取学生个人学情概览
  Future<ApiResponse<Map<String, dynamic>>> getStudentProfile() async {
    try {
      final response = await _dio.get('/profile/student/overview');
      return ApiResponse<Map<String, dynamic>>.fromJson(
        response.data,
        (data) => data as Map<String, dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取学情失败');
    }
  }

  /// 获取学习计划列表
  Future<ApiResponse<List<dynamic>>> getStudyPlans() async {
    try {
      final response = await _dio.get('/study-plan/list');
      return ApiResponse<List<dynamic>>.fromJson(
        response.data,
        (data) => data as List<dynamic>,
      );
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '获取学习计划失败');
    }
  }

  /// 更新学习计划进度
  Future<ApiResponse<void>> updateStudyPlanProgress({
    required String planId,
    required String taskId,
    required double progress,
  }) async {
    try {
      final response = await _dio.put('/study-plan/progress', data: {
        'plan_id': planId,
        'task_id': taskId,
        'progress': progress,
        'update_time': DateTime.now().toIso8601String(),
      });
      return ApiResponse<void>.fromJson(response.data, null);
    } on DioException catch (e) {
      return ApiResponse(code: -1, message: e.message ?? '更新进度失败');
    }
  }

  /// 登出，清除本地令牌
  Future<void> logout() async {
    try {
      await _dio.post('/auth/logout');
    } catch (_) {
      // 忽略登出请求失败
    }
    _accessToken = null;
    _refreshToken = null;
    await _secureStorage.delete(key: 'access_token');
    await _secureStorage.delete(key: 'refresh_token');
    _eventController.add('logged_out');
  }

  /// 释放资源
  void dispose() {
    _eventController.close();
    _dio.close();
  }
}
