package assignments

import (
	"boostai-backend/internal/assignmentgen"
	"boostai-backend/internal/handlers/api/shared"
	"boostai-backend/internal/sqlc"
	"encoding/json"
	"fmt"
	"math"
	"strings"

	"github.com/jackc/pgx/v5/pgtype"
)

func mapAssignment(assignment sqlc.Assignment) AssignmentResponse {
	return AssignmentResponse{
		ID:            assignment.ID,
		ClassroomID:   assignment.ClassroomID,
		TeacherID:     assignment.TeacherID,
		Title:         assignment.Title,
		Instructions:  shared.TextPointer(assignment.Instructions),
		PassThreshold: shared.NumericPointer(assignment.PassThreshold),
		Status:        string(assignment.Status),
		DueAt:         shared.TimePointer(assignment.DueAt),
		PublishedAt:   shared.TimePointer(assignment.PublishedAt),
		CreatedAt:     shared.TimePointer(assignment.CreatedAt),
		UpdatedAt:     shared.TimePointer(assignment.UpdatedAt),
	}
}

func parseQuestionTopicValue(value string) (sqlc.QuestionTopic, error) {
	switch strings.TrimSpace(strings.ToLower(value)) {
	case string(sqlc.QuestionTopicPlaceValue):
		return sqlc.QuestionTopicPlaceValue, nil
	case string(sqlc.QuestionTopicArithmetic):
		return sqlc.QuestionTopicArithmetic, nil
	case string(sqlc.QuestionTopicNegativeNumbers):
		return sqlc.QuestionTopicNegativeNumbers, nil
	case string(sqlc.QuestionTopicBidmas):
		return sqlc.QuestionTopicBidmas, nil
	case string(sqlc.QuestionTopicFractions):
		return sqlc.QuestionTopicFractions, nil
	case string(sqlc.QuestionTopicAlgebra):
		return sqlc.QuestionTopicAlgebra, nil
	case string(sqlc.QuestionTopicGeometry):
		return sqlc.QuestionTopicGeometry, nil
	case string(sqlc.QuestionTopicData):
		return sqlc.QuestionTopicData, nil
	default:
		return "", fmt.Errorf("primary_topic must be one of place_value, arithmetic, negative_numbers, bidmas, fractions, algebra, geometry, or data")
	}
}

func parseQuestionDifficultyValue(value string) (sqlc.QuestionDifficulty, error) {
	switch strings.TrimSpace(strings.ToLower(value)) {
	case string(sqlc.QuestionDifficultyEasy):
		return sqlc.QuestionDifficultyEasy, nil
	case string(sqlc.QuestionDifficultyMedium):
		return sqlc.QuestionDifficultyMedium, nil
	case string(sqlc.QuestionDifficultyHard):
		return sqlc.QuestionDifficultyHard, nil
	default:
		return "", fmt.Errorf("difficulty must be one of easy, medium, or hard")
	}
}

func parseQuestionStatusValue(value string) (sqlc.QuestionStatus, error) {
	switch strings.TrimSpace(strings.ToLower(value)) {
	case string(sqlc.QuestionStatusDraft):
		return sqlc.QuestionStatusDraft, nil
	case string(sqlc.QuestionStatusPublished):
		return sqlc.QuestionStatusPublished, nil
	case string(sqlc.QuestionStatusArchived):
		return sqlc.QuestionStatusArchived, nil
	default:
		return "", fmt.Errorf("question_status must be one of draft, published, or archived")
	}
}

func mapAssignmentGenerationWeaknessSummary(summary assignmentgen.WeaknessSummary) mixedPlanWeaknessSummaryResponse {
	topicScores := make(map[string]float64, len(summary.TopicScores))
	for topic, score := range summary.TopicScores {
		topicScores[string(topic)] = score
	}

	return mixedPlanWeaknessSummaryResponse{
		TopicScores:  topicScores,
		WeakTags:     append([]string(nil), summary.WeakTags...),
		RecentIssues: append([]string(nil), summary.RecentIssues...),
	}
}

func questionTopicPointer(topic sqlc.NullQuestionTopic) *string {
	if !topic.Valid {
		return nil
	}
	value := string(topic.QuestionTopic)
	return &value
}

func questionDifficultyPointer(difficulty sqlc.NullQuestionDifficulty) *string {
	if !difficulty.Valid {
		return nil
	}
	value := string(difficulty.QuestionDifficulty)
	return &value
}

func personalizedRatioValue(value *float64) float64 {
	if value == nil || *value == 0 {
		return 0.30
	}
	return *value
}

func int64Value(value *int64) int64 {
	if value == nil {
		return 0
	}
	return *value
}

func int64Pointer(value int64) *int64 {
	return &value
}

func stringPointer(value string) *string {
	return &value
}

func trimmedPointerValue(value *string) string {
	if value == nil {
		return ""
	}
	return strings.TrimSpace(*value)
}

func mapAssignmentQuestion(question sqlc.ListQuestionsForAssignmentRow) AssignmentQuestionResponse {
	return AssignmentQuestionResponse{
		AssignmentID:      question.AssignmentID,
		QuestionID:        question.QuestionID,
		Position:          question.Position,
		AuthorTeacherID:   question.AuthorTeacherID,
		Title:             question.Title,
		Prompt:            question.Prompt,
		Subject:           shared.TextPointer(question.Subject),
		Source:            shared.TextPointer(question.Source),
		QuestionStatus:    string(question.Status),
		QuestionCreatedAt: shared.TimePointer(question.CreatedAt),
		QuestionUpdatedAt: shared.TimePointer(question.UpdatedAt),
	}
}

func mapAssignmentStudentQuestionDetail(row sqlc.ListQuestionDetailsForAssignmentStudentRow, studentID int64) AssignmentStudentQuestionDetailResponse {
	var answerStatus *string
	if row.AnswerStatus.Valid {
		status := string(row.AnswerStatus.AnswerStatus)
		answerStatus = &status
	}

	var passStatus *string
	if row.PassStatus.Valid {
		status := string(row.PassStatus.AssignmentPassStatus)
		passStatus = &status
	}

	var passStatusOverride *string
	if row.PassStatusOverride.Valid {
		status := string(row.PassStatusOverride.AssignmentPassStatus)
		passStatusOverride = &status
	}

	var nextStepOutcome *string
	if row.NextStepOutcome.Valid {
		outcome := string(row.NextStepOutcome.AssignmentNextStepOutcome)
		nextStepOutcome = &outcome
	}

	var reviewNeedsAttention *bool
	if row.AnswerID.Valid {
		reviewNeedsAttention = shared.BoolPointer(row.ReviewNeedsAttention)
	}

	return AssignmentStudentQuestionDetailResponse{
		AssignmentID:              row.AssignmentID,
		StudentID:                 studentID,
		QuestionID:                row.QuestionID,
		Position:                  row.Position,
		Title:                     row.Title,
		Prompt:                    row.Prompt,
		Subject:                   shared.TextPointer(row.Subject),
		Source:                    shared.TextPointer(row.Source),
		QuestionTags:              row.QuestionTags,
		QuestionStatus:            string(row.QuestionStatus),
		CorrectAnswer:             shared.TextPointer(row.CorrectAnswer),
		AssignmentAiFeedback:      shared.TextPointer(row.AssignmentAiFeedback),
		AssignmentTeacherFeedback: shared.TextPointer(row.AssignmentTeacherFeedback),
		OverallScore:              shared.NumericPointer(row.OverallScore),
		PassThreshold:             shared.NumericPointer(row.PassThreshold),
		NextStepOutcome:           nextStepOutcome,
		PassStatusOverride:        passStatusOverride,
		PassStatus:                passStatus,
		AnswerID:                  shared.Int64Pointer(row.AnswerID),
		AnswerText:                shared.TextPointer(row.AnswerText),
		SolveMode:                 shared.TextPointer(row.SolveMode),
		WorkingSteps:              shared.TextPointer(row.WorkingSteps),
		IsCorrect:                 shared.BoolPointer(row.IsCorrect),
		AiFeedback:                shared.TextPointer(row.AiFeedback),
		TeacherFeedback:           shared.TextPointer(row.TeacherFeedback),
		AnswerStatus:              answerStatus,
		ReviewNeedsAttention:      reviewNeedsAttention,
		ReviewIssueReason:         shared.TextPointer(row.ReviewIssueReason),
		ReviewCorrectnessScore:    shared.NumericPointer(row.ReviewCorrectnessScore),
		ReviewUnderstandingScore:  shared.NumericPointer(row.ReviewUnderstandingScore),
		ReviewQuestionScore:       shared.NumericPointer(row.ReviewQuestionScore),
		ReviewConfidence:          shared.NumericPointer(row.ReviewConfidence),
		ReviewTags:                row.ReviewTags,
		SubmittedAt:               shared.TimePointer(row.SubmittedAt),
		ReviewedAt:                shared.TimePointer(row.ReviewedAt),
		AnswerCreatedAt:           shared.TimePointer(row.AnswerCreatedAt),
		AnswerUpdatedAt:           shared.TimePointer(row.AnswerUpdatedAt),
	}
}

func mapAssignmentReviewSummary(summary sqlc.GetAssignmentReviewSummaryRow) AssignmentReviewSummaryResponse {
	return AssignmentReviewSummaryResponse{
		AssignmentID:   summary.AssignmentID,
		TotalQuestions: summary.TotalQuestions,
		TotalAssigned:  summary.TotalAssigned,
		NotStarted:     summary.NotStarted,
		InProgress:     summary.InProgress,
		Submitted:      summary.Submitted,
		Reviewed:       summary.Reviewed,
	}
}

func mapAssignmentReviewQueueItem(row sqlc.ListAssignmentReviewQueueRow) AssignmentReviewQueueItemResponse {
	var nextStepOutcome *string
	if row.NextStepOutcome.Valid {
		outcome := string(row.NextStepOutcome.AssignmentNextStepOutcome)
		nextStepOutcome = &outcome
	}

	return AssignmentReviewQueueItemResponse{
		AssignmentID:        row.AssignmentID,
		StudentID:           row.StudentID,
		NextStepOutcome:     nextStepOutcome,
		StudentName:         row.StudentName,
		StudentEmail:        row.StudentEmail,
		TotalQuestions:      row.TotalQuestions,
		AnsweredQuestions:   row.AnsweredQuestions,
		ReviewedQuestions:   row.ReviewedQuestions,
		SubmittedQuestions:  row.SubmittedQuestions,
		InProgressQuestions: row.InProgressQuestions,
		ReviewStatus:        string(row.ReviewStatus),
		LatestSubmittedAt:   shared.TimePointer(row.LatestSubmittedAt),
		LatestReviewedAt:    shared.TimePointer(row.LatestReviewedAt),
	}
}

func buildAssignmentCloseReadiness(queue []sqlc.ListAssignmentReviewQueueRow) assignmentCloseReadiness {
	blockers := make([]string, 0)
	if len(queue) == 0 {
		return assignmentCloseReadiness{
			CanClose: false,
			Blockers: []string{"No students have been assigned yet."},
		}
	}

	for _, item := range queue {
		name := strings.TrimSpace(item.StudentName)
		if name == "" {
			name = fmt.Sprintf("Student %d", item.StudentID)
		}

		switch {
		case item.SubmittedQuestions > 0 || item.ReviewStatus == sqlc.AnswerStatusSubmitted:
			blockers = append(blockers, fmt.Sprintf("%s still has submitted work waiting for review.", name))
		case item.InProgressQuestions > 0 || item.ReviewStatus == sqlc.AnswerStatusInProgress:
			blockers = append(blockers, fmt.Sprintf("%s still has work in progress.", name))
		case item.AnsweredQuestions == 0 || item.ReviewStatus == sqlc.AnswerStatusNotStarted:
			blockers = append(blockers, fmt.Sprintf("%s has not started this assignment yet.", name))
		case !item.NextStepOutcome.Valid:
			blockers = append(blockers, fmt.Sprintf("%s still needs a next-step decision.", name))
		}
	}

	return assignmentCloseReadiness{
		CanClose: len(blockers) == 0,
		Blockers: blockers,
	}
}

func parseStoredRedoPlan(value string) (storedRedoPlan, error) {
	var payload storedRedoPlan
	if err := json.Unmarshal([]byte(value), &payload); err != nil {
		return storedRedoPlan{}, err
	}
	return payload, nil
}

func mapWeaknessSummary(studentID int64, summary weaknessSummary) StudentWeaknessSummaryResponse {
	return StudentWeaknessSummaryResponse{
		StudentID:    studentID,
		TopicScores:  summary.TopicScores,
		WeakTags:     summary.WeakTags,
		RecentIssues: summary.RecentIssues,
	}
}

func planningScore(isCorrect pgtype.Bool, understanding pgtype.Numeric) float64 {
	understandingValue := 0.0
	if value := shared.NumericPointer(understanding); value != nil {
		understandingValue = *value
	}
	correctnessValue := 0.0
	if isCorrect.Valid && isCorrect.Bool {
		correctnessValue = 1.0
	}
	return (correctnessValue + understandingValue) / 2
}

func roundToOneDecimal(value float64) float64 {
	return math.Round(value*10) / 10
}

func allowedQuestionTopics() []string {
	return []string{
		string(sqlc.QuestionTopicPlaceValue),
		string(sqlc.QuestionTopicArithmetic),
		string(sqlc.QuestionTopicNegativeNumbers),
		string(sqlc.QuestionTopicBidmas),
		string(sqlc.QuestionTopicFractions),
		string(sqlc.QuestionTopicAlgebra),
		string(sqlc.QuestionTopicGeometry),
		string(sqlc.QuestionTopicData),
	}
}

func emptyStringPointer(value string) *string {
	value = strings.TrimSpace(value)
	if value == "" {
		return nil
	}
	return &value
}

func minInt(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func isValidAssignmentPassStatus(value string) bool {
	switch value {
	case string(sqlc.AssignmentPassStatusPending), string(sqlc.AssignmentPassStatusPass), string(sqlc.AssignmentPassStatusNoPass):
		return true
	default:
		return false
	}
}

func isValidAssignmentNextStepOutcome(value string) bool {
	switch value {
	case "redo", "accept", "support":
		return true
	default:
		return false
	}
}

func pointerToFloat64(value float64) *float64 {
	return &value
}
