/// 自然写互动课堂手机端应用软件 V1.0
/// 云平台API服务 - 封装所有REST API通信逻辑
///
/// 功能说明：
/// 1. HTTP客户端配置（Dio拦截器、超时设置、重试策略）
/// 2. JWT Token自动管理（存储、刷新、过期处理）
/// 3. 请求签名（HMAC-SHA256防篡改）
/// 4. 证书锁定（Certificate Pinning防中间人攻击）
/// 5. 全部业务API封装（登录、作业、学情、消息等）
/// 6. 离线请求队列（断网时暂存请求，恢复后自动重放）

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

/* ========== 数据模型 ========== */

/// API响应统一包装
class ApiResponse<T> {
  final int code;          // 业务状态码（0=成功）
  final String message;    // 状态消息
  final T? data;           // 响应数据
  final int timestamp;     // 服务端时间戳

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

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

  /// 从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']) : null,
      timestamp: json['timestamp'] ?? 0,
    );
  }
}

/// 用户登录凭证
class AuthToken {
  final String accessToken;    // 访问令牌（有效期2小时）
  final String refreshToken;   // 刷新令牌（有效期7天）
  final int expiresAt;         // 访问令牌过期时间戳（毫秒）
  final String userRole;       // 用户角色: teacher / parent / admin

  AuthToken({
    required this.accessToken,
    required this.refreshToken,
    required this.expiresAt,
    required this.userRole,
  });

  /// 判断Token是否即将过期（提前5分钟刷新）
  bool get isExpiringSoon {
    return DateTime.now().millisecondsSinceEpoch > (expiresAt - 5 * 60 * 1000);
  }

  factory AuthToken.fromJson(Map<String, dynamic> json) {
    return AuthToken(
      accessToken: json['access_token'] ?? '',
      refreshToken: json['refresh_token'] ?? '',
      expiresAt: json['expires_at'] ?? 0,
      userRole: json['user_role'] ?? '',
    );
  }

  Map<String, dynamic> toJson() => {
    'access_token': accessToken,
    'refresh_token': refreshToken,
    'expires_at': expiresAt,
    'user_role': userRole,
  };
}

/// 用户信息模型
class UserInfo {
  final String userId;
  final String name;
  final String avatar;
  final String role;
  final String phone;
  final List<String> classIds;   // 关联的班级ID列表

  UserInfo({
    required this.userId,
    required this.name,
    required this.avatar,
    required this.role,
    required this.phone,
    required this.classIds,
  });

  factory UserInfo.fromJson(Map<String, dynamic> json) {
    return UserInfo(
      userId: json['user_id'] ?? '',
      name: json['name'] ?? '',
      avatar: json['avatar'] ?? '',
      role: json['role'] ?? '',
      phone: json['phone'] ?? '',
      classIds: List<String>.from(json['class_ids'] ?? []),
    );
  }
}

/// 作业信息模型
class AssignmentInfo {
  final String id;
  final String title;
  final String subject;        // 科目
  final String type;           // 类型: homework / exam / practice
  final String classId;
  final int publishTime;       // 发布时间
  final int deadline;          // 截止时间
  final int submittedCount;    // 已提交人数
  final int totalCount;        // 应提交人数
  final int status;            // 0=进行中, 1=已截止, 2=已批改

  AssignmentInfo({
    required this.id,
    required this.title,
    required this.subject,
    required this.type,
    required this.classId,
    required this.publishTime,
    required this.deadline,
    required this.submittedCount,
    required this.totalCount,
    required this.status,
  });

  factory AssignmentInfo.fromJson(Map<String, dynamic> json) {
    return AssignmentInfo(
      id: json['id'] ?? '',
      title: json['title'] ?? '',
      subject: json['subject'] ?? '',
      type: json['type'] ?? '',
      classId: json['class_id'] ?? '',
      publishTime: json['publish_time'] ?? 0,
      deadline: json['deadline'] ?? 0,
      submittedCount: json['submitted_count'] ?? 0,
      totalCount: json['total_count'] ?? 0,
      status: json['status'] ?? 0,
    );
  }
}

/// 学情报告模型
class LearningReport {
  final String studentId;
  final String studentName;
  final String subject;
  final double overallScore;             // 综合评分（0-100）
  final Map<String, double> knowledgeMap; // 知识点掌握度
  final List<ErrorItem> topErrors;       // 高频错题
  final WritingGrowth writingGrowth;     // 书写成长数据

  LearningReport({
    required this.studentId,
    required this.studentName,
    required this.subject,
    required this.overallScore,
    required this.knowledgeMap,
    required this.topErrors,
    required this.writingGrowth,
  });

  factory LearningReport.fromJson(Map<String, dynamic> json) {
    return LearningReport(
      studentId: json['student_id'] ?? '',
      studentName: json['student_name'] ?? '',
      subject: json['subject'] ?? '',
      overallScore: (json['overall_score'] ?? 0).toDouble(),
      knowledgeMap: Map<String, double>.from(json['knowledge_map'] ?? {}),
      topErrors: (json['top_errors'] as List? ?? [])
          .map((e) => ErrorItem.fromJson(e))
          .toList(),
      writingGrowth: WritingGrowth.fromJson(json['writing_growth'] ?? {}),
    );
  }
}

/// 错题条目
class ErrorItem {
  final String questionId;
  final String content;
  final String knowledgePoint;
  final int errorCount;
  final String errorReason;

  ErrorItem({
    required this.questionId,
    required this.content,
    required this.knowledgePoint,
    required this.errorCount,
    required this.errorReason,
  });

  factory ErrorItem.fromJson(Map<String, dynamic> json) {
    return ErrorItem(
      questionId: json['question_id'] ?? '',
      content: json['content'] ?? '',
      knowledgePoint: json['knowledge_point'] ?? '',
      errorCount: json['error_count'] ?? 0,
      errorReason: json['error_reason'] ?? '',
    );
  }
}

/// 书写成长数据
class WritingGrowth {
  final List<double> scores;        // 历次书写评分
  final List<String> dates;         // 对应日期
  final double strokeAccuracy;      // 笔顺正确率
  final double writingNeatness;     // 书写规范性
  final String improvement;         // 进步趋势描述

  WritingGrowth({
    required this.scores,
    required this.dates,
    required this.strokeAccuracy,
    required this.writingNeatness,
    required this.improvement,
  });

  factory WritingGrowth.fromJson(Map<String, dynamic> json) {
    return WritingGrowth(
      scores: List<double>.from(json['scores'] ?? []),
      dates: List<String>.from(json['dates'] ?? []),
      strokeAccuracy: (json['stroke_accuracy'] ?? 0).toDouble(),
      writingNeatness: (json['writing_neatness'] ?? 0).toDouble(),
      improvement: json['improvement'] ?? '',
    );
  }
}

/* ========== API服务实现 ========== */

/// 云平台API服务 - 管理所有HTTP通信
/// 采用Dio作为HTTP客户端，支持拦截器链、证书锁定、自动重试
class CloudApiService {
  /// 云平台API基础地址
  static const String _baseUrl = 'https://api.writech.com/v1';

  /// HMAC签名密钥（从安全存储中加载）
  final String _hmacSecret;

  /// 当前认证令牌
  AuthToken? _authToken;

  /// Token刷新锁（防止并发刷新）
  bool _isRefreshing = false;
  final List<Function> _refreshQueue = [];

  /// HTTP客户端实例
  late final HttpClient _httpClient;

  /// 离线请求队列（断网时暂存）
  final List<Map<String, dynamic>> _offlineQueue = [];

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

  CloudApiService({String hmacSecret = ''}) : _hmacSecret = hmacSecret {
    _httpClient = HttpClient()
      ..connectionTimeout = const Duration(seconds: 15)
      ..idleTimeout = const Duration(seconds: 60);

    // 配置证书锁定（防止中间人攻击）
    _httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) {
      // 验证证书指纹是否匹配预置的服务器证书
      final fingerprint = sha256.convert(cert.der).toString();
      const expectedFingerprint = 'a1b2c3d4e5f6...'; // 预置证书指纹
      return fingerprint == expectedFingerprint;
    };
  }

  /// 设置认证令牌（登录成功后调用）
  void setAuthToken(AuthToken token) {
    _authToken = token;
  }

  /// 生成请求签名（HMAC-SHA256）
  /// 签名内容: METHOD + PATH + TIMESTAMP + BODY_HASH
  String _generateSignature(String method, String path, int timestamp, String body) {
    final bodyHash = sha256.convert(utf8.encode(body)).toString();
    final content = '$method\n$path\n$timestamp\n$bodyHash';
    final hmacSha256 = Hmac(sha256, utf8.encode(_hmacSecret));
    return hmacSha256.convert(utf8.encode(content)).toString();
  }

  /// 统一HTTP请求方法（带签名、Token、重试）
  Future<ApiResponse<T>> _request<T>({
    required String method,
    required String path,
    Map<String, dynamic>? queryParams,
    Map<String, dynamic>? body,
    T Function(dynamic)? fromData,
    int retryCount = 0,
  }) async {
    // 检查Token是否需要刷新
    if (_authToken != null && _authToken!.isExpiringSoon) {
      await _refreshToken();
    }

    final uri = Uri.parse('$_baseUrl$path').replace(queryParameters: 
      queryParams?.map((k, v) => MapEntry(k, v.toString())));
    final timestamp = DateTime.now().millisecondsSinceEpoch;
    final bodyStr = body != null ? jsonEncode(body) : '';
    final signature = _generateSignature(method, path, timestamp, bodyStr);

    try {
      final request = await _httpClient.openUrl(method, uri);

      // 设置请求头
      request.headers.set('Content-Type', 'application/json');
      request.headers.set('X-Timestamp', timestamp.toString());
      request.headers.set('X-Signature', signature);
      request.headers.set('X-Client', 'writech-mobile/1.0');
      if (_authToken != null) {
        request.headers.set('Authorization', 'Bearer ${_authToken!.accessToken}');
      }

      // 写入请求体
      if (body != null) {
        request.write(bodyStr);
      }

      // 发送请求并接收响应
      final response = await request.close();
      final responseBody = await response.transform(utf8.decoder).join();
      final jsonData = jsonDecode(responseBody) as Map<String, dynamic>;

      // 处理401未授权（Token过期）
      if (response.statusCode == 401 && retryCount < 1) {
        await _refreshToken();
        return _request(
          method: method, path: path, queryParams: queryParams,
          body: body, fromData: fromData, retryCount: retryCount + 1,
        );
      }

      return ApiResponse.fromJson(jsonData, fromData);
    } on SocketException {
      // 网络不可用，加入离线队列
      if (method == 'POST' || method == 'PUT') {
        _offlineQueue.add({
          'method': method, 'path': path,
          'body': body, 'timestamp': timestamp,
        });
      }
      return ApiResponse(code: -1, message: '网络连接不可用', timestamp: timestamp);
    } catch (e) {
      // 重试逻辑（指数退避）
      if (retryCount < _maxRetries) {
        await Future.delayed(Duration(seconds: 1 << retryCount));
        return _request(
          method: method, path: path, queryParams: queryParams,
          body: body, fromData: fromData, retryCount: retryCount + 1,
        );
      }
      return ApiResponse(code: -1, message: '请求失败: $e', timestamp: timestamp);
    }
  }

  /// 刷新Token（使用Refresh Token获取新的Access Token）
  Future<void> _refreshToken() async {
    if (_isRefreshing) {
      // 等待正在进行的刷新完成
      final completer = Completer<void>();
      _refreshQueue.add(() => completer.complete());
      return completer.future;
    }

    _isRefreshing = true;
    try {
      final response = await _request<AuthToken>(
        method: 'POST',
        path: '/auth/refresh',
        body: {'refresh_token': _authToken?.refreshToken ?? ''},
        fromData: (data) => AuthToken.fromJson(data),
      );

      if (response.isSuccess && response.data != null) {
        _authToken = response.data;
        // 持久化新Token到安全存储
        _persistToken(_authToken!);
      }
    } finally {
      _isRefreshing = false;
      // 通知所有等待的请求继续
      for (final callback in _refreshQueue) {
        callback();
      }
      _refreshQueue.clear();
    }
  }

  /// 持久化Token到Keychain/KeyStore
  void _persistToken(AuthToken token) {
    // 使用flutter_secure_storage存储到系统安全存储
    // iOS: Keychain  Android: KeyStore
  }

  /// 重放离线队列中的请求（网络恢复后调用）
  Future<int> replayOfflineQueue() async {
    int successCount = 0;
    final queue = List<Map<String, dynamic>>.from(_offlineQueue);
    _offlineQueue.clear();

    for (final item in queue) {
      final response = await _request(
        method: item['method'],
        path: item['path'],
        body: item['body'],
      );
      if (response.isSuccess) successCount++;
    }
    return successCount;
  }

  /* ========== 认证相关API ========== */

  /// 手机号+验证码登录
  Future<ApiResponse<AuthToken>> loginByPhone(String phone, String code) {
    return _request(
      method: 'POST',
      path: '/auth/login/phone',
      body: {'phone': phone, 'code': code},
      fromData: (data) => AuthToken.fromJson(data),
    );
  }

  /// 微信OAuth登录
  Future<ApiResponse<AuthToken>> loginByWechat(String wxCode) {
    return _request(
      method: 'POST',
      path: '/auth/login/wechat',
      body: {'wx_code': wxCode},
      fromData: (data) => AuthToken.fromJson(data),
    );
  }

  /// 获取当前用户信息
  Future<ApiResponse<UserInfo>> getUserInfo() {
    return _request(
      method: 'GET',
      path: '/user/profile',
      fromData: (data) => UserInfo.fromJson(data),
    );
  }

  /// 登出（撤销Token）
  Future<ApiResponse> logout() {
    return _request(method: 'POST', path: '/auth/logout');
  }

  /* ========== 作业相关API ========== */

  /// 获取作业列表（教师端）
  Future<ApiResponse<List<AssignmentInfo>>> getAssignmentList({
    required String classId,
    int page = 1,
    int pageSize = 20,
    String? status,
  }) {
    return _request(
      method: 'GET',
      path: '/assignment/list',
      queryParams: {
        'class_id': classId,
        'page': page,
        'page_size': pageSize,
        if (status != null) 'status': status,
      },
      fromData: (data) => (data as List)
          .map((e) => AssignmentInfo.fromJson(e))
          .toList(),
    );
  }

  /// 发布新作业（教师端）
  Future<ApiResponse<String>> publishAssignment({
    required String title,
    required String classId,
    required String subject,
    required int deadline,
    required List<Map<String, dynamic>> questions,
  }) {
    return _request(
      method: 'POST',
      path: '/assignment/publish',
      body: {
        'title': title,
        'class_id': classId,
        'subject': subject,
        'deadline': deadline,
        'questions': questions,
      },
    );
  }

  /* ========== 学情报告API ========== */

  /// 获取学生学情报告（家长端/教师端）
  Future<ApiResponse<LearningReport>> getStudentReport(String studentId, {String? subject}) {
    return _request(
      method: 'GET',
      path: '/report/student/$studentId',
      queryParams: subject != null ? {'subject': subject} : null,
      fromData: (data) => LearningReport.fromJson(data),
    );
  }

  /// 获取班级学情概览（教师端）
  Future<ApiResponse<Map<String, dynamic>>> getClassReport(String classId) {
    return _request(
      method: 'GET',
      path: '/report/class/$classId',
    );
  }

  /* ========== 消息通知API ========== */

  /// 获取消息列表
  Future<ApiResponse<List<Map<String, dynamic>>>> getMessageList({
    int page = 1,
    int pageSize = 20,
  }) {
    return _request(
      method: 'GET',
      path: '/message/list',
      queryParams: {'page': page, 'page_size': pageSize},
    );
  }

  /// 发送家校沟通消息（教师→家长）
  Future<ApiResponse> sendMessage({
    required String toUserId,
    required String content,
    String type = 'text',
  }) {
    return _request(
      method: 'POST',
      path: '/message/send',
      body: {'to_user_id': toUserId, 'content': content, 'type': type},
    );
  }

  /// 标记消息已读
  Future<ApiResponse> markMessageRead(List<String> messageIds) {
    return _request(
      method: 'PUT',
      path: '/message/read',
      body: {'message_ids': messageIds},
    );
  }

  /* ========== 笔迹数据API ========== */

  /// 上传笔迹数据（教师端蓝牙收笔后上传）
  Future<ApiResponse<String>> uploadStrokeData({
    required String assignmentId,
    required String studentId,
    required List<Map<String, dynamic>> strokes,
  }) {
    return _request(
      method: 'POST',
      path: '/stroke/upload',
      body: {
        'assignment_id': assignmentId,
        'student_id': studentId,
        'strokes': strokes,
        'client_time': DateTime.now().millisecondsSinceEpoch,
      },
    );
  }

  /// 获取笔迹回放数据
  Future<ApiResponse<List<Map<String, dynamic>>>> getStrokeReplay({
    required String assignmentId,
    required String studentId,
  }) {
    return _request(
      method: 'GET',
      path: '/stroke/replay',
      queryParams: {
        'assignment_id': assignmentId,
        'student_id': studentId,
      },
    );
  }

  /// 销毁HTTP客户端
  void dispose() {
    _httpClient.close();
    _offlineQueue.clear();
    _refreshQueue.clear();
  }
}
