import 'dart:math';
import '../models/course.dart';
import '../models/questionnaire.dart';
import '../models/evaluation_result.dart';
import 'aufe_connection.dart';
import 'questionnaire_parser.dart';
import '../utils/text_generator.dart';

/// Service for handling course evaluation operations
/// Manages the evaluation process including form data building,
/// option selection, and text generation
class EvaluationService {
  final AUFEConnection _connection;
  final QuestionnaireParser _parser;
  final TextGenerator _textGenerator;
  final Random _random = Random();

  EvaluationService({
    required AUFEConnection connection,
    required QuestionnaireParser parser,
    required TextGenerator textGenerator,
  }) : _connection = connection,
       _parser = parser,
       _textGenerator = textGenerator;

  /// Prepare evaluation for a course (access page, parse questionnaire, generate answers)
  ///
  /// [course] - The course to evaluate
  /// [totalCourses] - Total number of courses (for count parameter)
  /// Returns form data map if successful, null otherwise
  Future<Map<String, String>?> prepareEvaluation(
    Course course, {
    int? totalCourses,
  }) async {
    try {
      // 1. Access evaluation page to get questionnaire HTML
      final html = await _connection.accessEvaluationPage(
        course,
        totalCourses: totalCourses,
      );
      if (html == null) {
        return null;
      }

      // 2. Parse questionnaire
      final questionnaire = await _parser.parseAsync(html);

      // 3. Build form data
      final formData = _buildFormData(
        questionnaire,
        course: course,
        totalCourses: totalCourses,
      );

      return formData;
    } catch (e, stackTrace) {
      print('❌ prepareEvaluation error: $e');
      print('❌ Stack trace: $stackTrace');
      return null;
    }
  }

  /// Submit evaluation with prepared form data
  ///
  /// [course] - The course being evaluated
  /// [formData] - Prepared form data from prepareEvaluation
  /// Returns [EvaluationResult] with success status and error message if failed
  Future<EvaluationResult> submitEvaluation(
    Course course,
    Map<String, String> formData,
  ) async {
    try {
      final submitResponse = await _connection.submitEvaluation(formData);

      if (submitResponse.isSuccess) {
        return EvaluationResult(course: course, success: true);
      } else {
        return EvaluationResult(
          course: course,
          success: false,
          errorMessage: submitResponse.msg,
        );
      }
    } catch (e, stackTrace) {
      print('❌ submitEvaluation error: $e');
      print('❌ Stack trace: $stackTrace');
      return EvaluationResult(
        course: course,
        success: false,
        errorMessage: '提交评价出错: $e',
      );
    }
  }

  /// Evaluate a single course (legacy method, combines prepare and submit with countdown)
  ///
  /// [course] - The course to evaluate
  /// [totalCourses] - Total number of courses (for count parameter)
  /// [onStatusChange] - Optional callback to report status changes during evaluation
  /// [onCountdown] - Optional callback to report countdown progress (seconds remaining, total seconds)
  /// Returns [EvaluationResult] with success status and error message if failed
  Future<EvaluationResult> evaluateCourse(
    Course course, {
    int? totalCourses,
    Function(String status)? onStatusChange,
    Function(int remaining, int total)? onCountdown,
  }) async {
    try {
      // 1. Prepare evaluation
      onStatusChange?.call('正在访问评价页面...');
      final formData = await prepareEvaluation(
        course,
        totalCourses: totalCourses,
      );
      if (formData == null) {
        return EvaluationResult(
          course: course,
          success: false,
          errorMessage: '无法访问评价页面',
        );
      }

      onStatusChange?.call('正在解析问卷结构...');
      onStatusChange?.call('正在生成答案...');

      // 2. Wait 140 seconds before submission (server anti-bot requirement)
      onStatusChange?.call('等待提交中...');

      const totalSeconds = 140;
      for (int i = totalSeconds; i > 0; i--) {
        onCountdown?.call(i, totalSeconds);

        // Also report to status for logging
        final progress = (totalSeconds - i) / totalSeconds;
        final barLength = 20;
        final filledLength = (progress * barLength).round();
        final bar = '█' * filledLength + '░' * (barLength - filledLength);
        final percent = (progress * 100).toInt();
        onStatusChange?.call('等待提交 [$bar] $percent% (${i}s)');

        await Future.delayed(const Duration(seconds: 1));
      }

      // 3. Submit evaluation
      onStatusChange?.call('正在提交评价...');
      final result = await submitEvaluation(course, formData);

      if (result.success) {
        onStatusChange?.call('提交成功');
      } else {
        onStatusChange?.call('提交失败: ${result.errorMessage}');
      }

      return result;
    } catch (e, stackTrace) {
      print('❌ evaluateCourse error: $e');
      print('❌ Stack trace: $stackTrace');
      onStatusChange?.call('评教失败');
      return EvaluationResult(
        course: course,
        success: false,
        errorMessage: '评教过程出错: $e',
      );
    }
  }

  /// Build form data from questionnaire
  ///
  /// Automatically selects best options for radio questions
  /// and generates appropriate text for text questions
  Map<String, String> _buildFormData(
    Questionnaire questionnaire, {
    required Course course,
    int? totalCourses,
  }) {
    final formData = <String, String>{};

    // Add required metadata fields (matching the correct request format)
    // Use Course data as primary source, fallback to questionnaire parsed data
    formData['optType'] = 'submit';
    formData['tokenValue'] = questionnaire.tokenValue;
    formData['questionnaireCode'] = course.questionnaireCode.isNotEmpty
        ? course.questionnaireCode
        : questionnaire.questionnaireCode;
    formData['evaluationContent'] = course.evaluationContentNumber.isNotEmpty
        ? course.evaluationContentNumber
        : questionnaire.evaluationContent;
    formData['evaluatedPeopleNumber'] = course.evaluatedPeopleNumber.isNotEmpty
        ? course.evaluatedPeopleNumber
        : questionnaire.evaluatedPeopleNumber;
    formData['count'] = totalCourses?.toString() ?? '';

    // Process radio questions - select best option for each
    for (final question in questionnaire.radioQuestions) {
      final selectedOption = _selectBestOption(question.options);
      formData[question.key] = selectedOption.value;
    }

    // Process text questions - generate appropriate text
    for (final question in questionnaire.textQuestions) {
      final generatedText = _generateTextAnswer(question);
      formData[question.key] = generatedText;
    }

    print('📝 Form data keys: ${formData.keys.join(", ")}');
    print('📝 Metadata fields:');
    print('   optType = ${formData['optType']}');
    print('   tokenValue = ${formData['tokenValue']}');
    print('   questionnaireCode = ${formData['questionnaireCode']}');
    print('   evaluationContent = ${formData['evaluationContent']}');
    print('   evaluatedPeopleNumber = ${formData['evaluatedPeopleNumber']}');
    print('   count = ${formData['count']}');
    print(
      '📝 Question answers (${questionnaire.radioQuestions.length} radio + ${questionnaire.textQuestions.length} text):',
    );
    formData.forEach((key, value) {
      if (key.startsWith('0000') || key == 'zgpj') {
        print('   $key = $value');
      }
    });

    return formData;
  }

  /// Select the best option from a list of radio options
  ///
  /// Strategy: Prefer weight 1.0 options (80% probability)
  /// Otherwise select second highest weight option (20% probability)
  ///
  /// [options] - List of available radio options
  /// Returns the selected [RadioOption]
  RadioOption _selectBestOption(List<RadioOption> options) {
    if (options.isEmpty) {
      throw ArgumentError('Options list cannot be empty');
    }

    // Sort options by weight in descending order
    final sortedOptions = List<RadioOption>.from(options)
      ..sort((a, b) => b.weight.compareTo(a.weight));

    // Find options with weight 1.0
    final weight1Options = sortedOptions
        .where((opt) => opt.weight == 1.0)
        .toList();

    // If there are weight 1.0 options
    if (weight1Options.isNotEmpty) {
      // 80% chance to select weight 1.0 option
      if (_random.nextDouble() < 0.8) {
        // If multiple weight 1.0 options, randomly select one
        return weight1Options[_random.nextInt(weight1Options.length)];
      }
    }

    // 20% chance or no weight 1.0 options: select second highest weight
    if (sortedOptions.length > 1) {
      // Find second highest weight
      final highestWeight = sortedOptions[0].weight;
      final secondHighestOptions = sortedOptions
          .where((opt) => opt.weight < highestWeight)
          .toList();

      if (secondHighestOptions.isNotEmpty) {
        // Get all options with second highest weight
        final secondHighestWeight = secondHighestOptions[0].weight;
        final secondHighestGroup = secondHighestOptions
            .where((opt) => opt.weight == secondHighestWeight)
            .toList();

        return secondHighestGroup[_random.nextInt(secondHighestGroup.length)];
      }
    }

    // Fallback: return highest weight option
    return sortedOptions[0];
  }

  /// Generate text answer for a text question
  ///
  /// Uses TextGenerator to create appropriate text based on question type
  /// Validates the generated text meets all requirements
  ///
  /// [question] - The text question to answer
  /// Returns generated text string
  String _generateTextAnswer(TextQuestion question) {
    // Generate text based on question type
    String text = _textGenerator.generate(question.type);

    // Validate the generated text
    // If validation fails, try again (up to 3 attempts)
    int attempts = 0;
    while (!_textGenerator.validate(text) && attempts < 3) {
      text = _textGenerator.generate(question.type);
      attempts++;
    }

    return text;
  }

  /// Batch evaluate all pending courses
  ///
  /// [onProgress] - Callback function to report progress with detailed status
  /// Returns [BatchEvaluationResult] with statistics and individual results
  Future<BatchEvaluationResult> batchEvaluate({
    required Function(int current, int total, Course course, String status)
    onProgress,
  }) async {
    final startTime = DateTime.now();
    final results = <EvaluationResult>[];

    try {
      // 1. Fetch all pending courses
      final courses = await _connection.fetchCourseList();

      // Filter out already evaluated courses
      final pendingCourses = courses.where((c) => !c.isEvaluated).toList();
      final total = pendingCourses.length;

      if (total == 0) {
        return BatchEvaluationResult(
          total: 0,
          success: 0,
          failed: 0,
          results: [],
          duration: DateTime.now().difference(startTime),
        );
      }

      // 2. Evaluate each course
      for (int i = 0; i < pendingCourses.length; i++) {
        final course = pendingCourses[i];

        // Evaluate the course with status updates
        final result = await evaluateCourse(
          course,
          totalCourses: total,
          onStatusChange: (status) {
            // Report progress with current course and status
            // Use i (not i+1) to show completed count, current course is still in progress
            onProgress(i, total, course, status);
          },
        );
        results.add(result);

        // Check if evaluation failed
        if (!result.success) {
          // Report failure
          onProgress(i + 1, total, course, '评教失败，任务中断');

          // Stop batch evaluation on first error
          final successCount = results.where((r) => r.success).length;
          final failedCount = results.where((r) => !r.success).length;
          final duration = DateTime.now().difference(startTime);

          return BatchEvaluationResult(
            total: i + 1, // Only count evaluated courses
            success: successCount,
            failed: failedCount,
            results: results,
            duration: duration,
          );
        }

        // Verify evaluation by checking course list
        onProgress(i + 1, total, course, '验证评教结果');
        final updatedCourses = await _connection.fetchCourseList();
        final updatedCourse = updatedCourses.firstWhere(
          (c) => c.id == course.id,
          orElse: () => course,
        );

        if (!updatedCourse.isEvaluated) {
          // Evaluation not confirmed, treat as failure
          results[results.length - 1] = EvaluationResult(
            course: course,
            success: false,
            errorMessage: '评教未生效，服务器未确认',
          );

          onProgress(i + 1, total, course, '评教未生效，任务中断');

          final successCount = results.where((r) => r.success).length;
          final failedCount = results.where((r) => !r.success).length;
          final duration = DateTime.now().difference(startTime);

          return BatchEvaluationResult(
            total: i + 1,
            success: successCount,
            failed: failedCount,
            results: results,
            duration: duration,
          );
        }

        // Report successful completion
        onProgress(i + 1, total, course, '评教完成');

        // Small delay between evaluations to avoid overwhelming the server
        if (i < pendingCourses.length - 1) {
          await Future.delayed(const Duration(milliseconds: 500));
        }
      }

      // 3. Calculate statistics
      final successCount = results.where((r) => r.success).length;
      final failedCount = results.where((r) => !r.success).length;
      final duration = DateTime.now().difference(startTime);

      return BatchEvaluationResult(
        total: total,
        success: successCount,
        failed: failedCount,
        results: results,
        duration: duration,
      );
    } catch (e) {
      // If batch evaluation fails completely, return partial results
      final successCount = results.where((r) => r.success).length;
      final failedCount = results.where((r) => !r.success).length;
      final duration = DateTime.now().difference(startTime);

      return BatchEvaluationResult(
        total: results.length,
        success: successCount,
        failed: failedCount,
        results: results,
        duration: duration,
      );
    }
  }
}
