package training

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
)

// CreateAssignment creates a new training assignment
func (s *Store) CreateAssignment(ctx context.Context, assignment *TrainingAssignment) error {
	assignment.ID = uuid.New()
	assignment.CreatedAt = time.Now().UTC()
	assignment.UpdatedAt = assignment.CreatedAt
	if assignment.Status == "" {
		assignment.Status = AssignmentStatusPending
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO training_assignments (
			id, tenant_id, module_id, user_id, user_name, user_email,
			role_code, trigger_type, trigger_event, status, progress_percent,
			quiz_score, quiz_passed, quiz_attempts,
			started_at, completed_at, deadline, certificate_id,
			escalation_level, last_escalation_at, enrollment_id,
			created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5, $6,
			$7, $8, $9, $10, $11,
			$12, $13, $14,
			$15, $16, $17, $18,
			$19, $20, $21,
			$22, $23
		)
	`,
		assignment.ID, assignment.TenantID, assignment.ModuleID, assignment.UserID, assignment.UserName, assignment.UserEmail,
		assignment.RoleCode, string(assignment.TriggerType), assignment.TriggerEvent, string(assignment.Status), assignment.ProgressPercent,
		assignment.QuizScore, assignment.QuizPassed, assignment.QuizAttempts,
		assignment.StartedAt, assignment.CompletedAt, assignment.Deadline, assignment.CertificateID,
		assignment.EscalationLevel, assignment.LastEscalationAt, assignment.EnrollmentID,
		assignment.CreatedAt, assignment.UpdatedAt,
	)

	return err
}

// GetAssignment retrieves an assignment by ID
func (s *Store) GetAssignment(ctx context.Context, id uuid.UUID) (*TrainingAssignment, error) {
	var a TrainingAssignment
	var status, triggerType string

	err := s.pool.QueryRow(ctx, `
		SELECT
			ta.id, ta.tenant_id, ta.module_id, ta.user_id, ta.user_name, ta.user_email,
			ta.role_code, ta.trigger_type, ta.trigger_event, ta.status, ta.progress_percent,
			ta.quiz_score, ta.quiz_passed, ta.quiz_attempts,
			ta.started_at, ta.completed_at, ta.deadline, ta.certificate_id,
			ta.escalation_level, ta.last_escalation_at, ta.enrollment_id,
			ta.created_at, ta.updated_at,
			m.module_code, m.title
		FROM training_assignments ta
		JOIN training_modules m ON m.id = ta.module_id
		WHERE ta.id = $1
	`, id).Scan(
		&a.ID, &a.TenantID, &a.ModuleID, &a.UserID, &a.UserName, &a.UserEmail,
		&a.RoleCode, &triggerType, &a.TriggerEvent, &status, &a.ProgressPercent,
		&a.QuizScore, &a.QuizPassed, &a.QuizAttempts,
		&a.StartedAt, &a.CompletedAt, &a.Deadline, &a.CertificateID,
		&a.EscalationLevel, &a.LastEscalationAt, &a.EnrollmentID,
		&a.CreatedAt, &a.UpdatedAt,
		&a.ModuleCode, &a.ModuleTitle,
	)

	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	a.Status = AssignmentStatus(status)
	a.TriggerType = TriggerType(triggerType)
	return &a, nil
}

// ListAssignments lists assignments for a tenant with optional filters
func (s *Store) ListAssignments(ctx context.Context, tenantID uuid.UUID, filters *AssignmentFilters) ([]TrainingAssignment, int, error) {
	countQuery := "SELECT COUNT(*) FROM training_assignments WHERE tenant_id = $1"
	countArgs := []interface{}{tenantID}
	countArgIdx := 2

	query := `
		SELECT
			ta.id, ta.tenant_id, ta.module_id, ta.user_id, ta.user_name, ta.user_email,
			ta.role_code, ta.trigger_type, ta.trigger_event, ta.status, ta.progress_percent,
			ta.quiz_score, ta.quiz_passed, ta.quiz_attempts,
			ta.started_at, ta.completed_at, ta.deadline, ta.certificate_id,
			ta.escalation_level, ta.last_escalation_at, ta.enrollment_id,
			ta.created_at, ta.updated_at,
			m.module_code, m.title
		FROM training_assignments ta
		JOIN training_modules m ON m.id = ta.module_id
		WHERE ta.tenant_id = $1`

	args := []interface{}{tenantID}
	argIdx := 2

	if filters != nil {
		if filters.ModuleID != nil {
			query += fmt.Sprintf(" AND ta.module_id = $%d", argIdx)
			args = append(args, *filters.ModuleID)
			argIdx++
			countQuery += fmt.Sprintf(" AND module_id = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.ModuleID)
			countArgIdx++
		}
		if filters.UserID != nil {
			query += fmt.Sprintf(" AND ta.user_id = $%d", argIdx)
			args = append(args, *filters.UserID)
			argIdx++
			countQuery += fmt.Sprintf(" AND user_id = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.UserID)
			countArgIdx++
		}
		if filters.RoleCode != "" {
			query += fmt.Sprintf(" AND ta.role_code = $%d", argIdx)
			args = append(args, filters.RoleCode)
			argIdx++
			countQuery += fmt.Sprintf(" AND role_code = $%d", countArgIdx)
			countArgs = append(countArgs, filters.RoleCode)
			countArgIdx++
		}
		if filters.Status != "" {
			query += fmt.Sprintf(" AND ta.status = $%d", argIdx)
			args = append(args, string(filters.Status))
			argIdx++
			countQuery += fmt.Sprintf(" AND status = $%d", countArgIdx)
			countArgs = append(countArgs, string(filters.Status))
			countArgIdx++
		}
		if filters.Overdue != nil && *filters.Overdue {
			query += " AND ta.deadline < NOW() AND ta.status IN ('pending', 'in_progress')"
			countQuery += " AND deadline < NOW() AND status IN ('pending', 'in_progress')"
		}
	}

	var total int
	err := s.pool.QueryRow(ctx, countQuery, countArgs...).Scan(&total)
	if err != nil {
		return nil, 0, err
	}

	query += " ORDER BY ta.deadline ASC"

	if filters != nil && filters.Limit > 0 {
		query += fmt.Sprintf(" LIMIT $%d", argIdx)
		args = append(args, filters.Limit)
		argIdx++
		if filters.Offset > 0 {
			query += fmt.Sprintf(" OFFSET $%d", argIdx)
			args = append(args, filters.Offset)
			argIdx++
		}
	}

	rows, err := s.pool.Query(ctx, query, args...)
	if err != nil {
		return nil, 0, err
	}
	defer rows.Close()

	var assignments []TrainingAssignment
	for rows.Next() {
		var a TrainingAssignment
		var status, triggerType string

		err := rows.Scan(
			&a.ID, &a.TenantID, &a.ModuleID, &a.UserID, &a.UserName, &a.UserEmail,
			&a.RoleCode, &triggerType, &a.TriggerEvent, &status, &a.ProgressPercent,
			&a.QuizScore, &a.QuizPassed, &a.QuizAttempts,
			&a.StartedAt, &a.CompletedAt, &a.Deadline, &a.CertificateID,
			&a.EscalationLevel, &a.LastEscalationAt, &a.EnrollmentID,
			&a.CreatedAt, &a.UpdatedAt,
			&a.ModuleCode, &a.ModuleTitle,
		)
		if err != nil {
			return nil, 0, err
		}

		a.Status = AssignmentStatus(status)
		a.TriggerType = TriggerType(triggerType)
		assignments = append(assignments, a)
	}

	if assignments == nil {
		assignments = []TrainingAssignment{}
	}

	return assignments, total, nil
}

// UpdateAssignmentStatus updates the status and related fields
func (s *Store) UpdateAssignmentStatus(ctx context.Context, id uuid.UUID, status AssignmentStatus, progress int) error {
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		UPDATE training_assignments SET
			status = $2,
			progress_percent = $3,
			started_at = CASE
				WHEN started_at IS NULL AND $2 IN ('in_progress', 'completed') THEN $4
				ELSE started_at
			END,
			completed_at = CASE
				WHEN $2 = 'completed' THEN $4
				ELSE completed_at
			END,
			updated_at = $4
		WHERE id = $1
	`, id, string(status), progress, now)

	return err
}

// UpdateAssignmentDeadline updates the deadline of an assignment
func (s *Store) UpdateAssignmentDeadline(ctx context.Context, id uuid.UUID, deadline time.Time) error {
	now := time.Now().UTC()
	_, err := s.pool.Exec(ctx, `
		UPDATE training_assignments SET
			deadline = $2,
			updated_at = $3
		WHERE id = $1
	`, id, deadline, now)
	return err
}

// UpdateAssignmentQuizResult updates quiz-related fields on an assignment
func (s *Store) UpdateAssignmentQuizResult(ctx context.Context, id uuid.UUID, score float64, passed bool, attempts int) error {
	now := time.Now().UTC()

	_, err := s.pool.Exec(ctx, `
		UPDATE training_assignments SET
			quiz_score = $2,
			quiz_passed = $3,
			quiz_attempts = $4,
			status = CASE WHEN $3 = true THEN 'completed' ELSE status END,
			completed_at = CASE WHEN $3 = true THEN $5 ELSE completed_at END,
			progress_percent = CASE WHEN $3 = true THEN 100 ELSE progress_percent END,
			updated_at = $5
		WHERE id = $1
	`, id, score, passed, attempts, now)

	return err
}

// ListOverdueAssignments returns assignments past their deadline
func (s *Store) ListOverdueAssignments(ctx context.Context, tenantID uuid.UUID) ([]TrainingAssignment, error) {
	overdue := true
	assignments, _, err := s.ListAssignments(ctx, tenantID, &AssignmentFilters{
		Overdue: &overdue,
		Limit:   1000,
	})
	return assignments, err
}

// SetCertificateID sets the certificate ID on an assignment
func (s *Store) SetCertificateID(ctx context.Context, assignmentID, certID uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE training_assignments SET certificate_id = $2, updated_at = NOW() WHERE id = $1
	`, assignmentID, certID)
	return err
}

// GetAssignmentByCertificateID finds an assignment by its certificate ID
func (s *Store) GetAssignmentByCertificateID(ctx context.Context, certID uuid.UUID) (*TrainingAssignment, error) {
	var assignmentID uuid.UUID
	err := s.pool.QueryRow(ctx,
		"SELECT id FROM training_assignments WHERE certificate_id = $1",
		certID).Scan(&assignmentID)
	if err == pgx.ErrNoRows {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return s.GetAssignment(ctx, assignmentID)
}

// ListCertificates lists assignments that have certificates for a tenant
func (s *Store) ListCertificates(ctx context.Context, tenantID uuid.UUID) ([]TrainingAssignment, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT
			ta.id, ta.tenant_id, ta.module_id, ta.user_id, ta.user_name, ta.user_email,
			ta.role_code, ta.trigger_type, ta.trigger_event, ta.status, ta.progress_percent,
			ta.quiz_score, ta.quiz_passed, ta.quiz_attempts,
			ta.started_at, ta.completed_at, ta.deadline, ta.certificate_id,
			ta.escalation_level, ta.last_escalation_at, ta.enrollment_id,
			ta.created_at, ta.updated_at,
			m.module_code, m.title
		FROM training_assignments ta
		JOIN training_modules m ON m.id = ta.module_id
		WHERE ta.tenant_id = $1 AND ta.certificate_id IS NOT NULL
		ORDER BY ta.completed_at DESC
	`, tenantID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var assignments []TrainingAssignment
	for rows.Next() {
		var a TrainingAssignment
		var status, triggerType string

		err := rows.Scan(
			&a.ID, &a.TenantID, &a.ModuleID, &a.UserID, &a.UserName, &a.UserEmail,
			&a.RoleCode, &triggerType, &a.TriggerEvent, &status, &a.ProgressPercent,
			&a.QuizScore, &a.QuizPassed, &a.QuizAttempts,
			&a.StartedAt, &a.CompletedAt, &a.Deadline, &a.CertificateID,
			&a.EscalationLevel, &a.LastEscalationAt, &a.EnrollmentID,
			&a.CreatedAt, &a.UpdatedAt,
			&a.ModuleCode, &a.ModuleTitle,
		)
		if err != nil {
			return nil, err
		}

		a.Status = AssignmentStatus(status)
		a.TriggerType = TriggerType(triggerType)
		assignments = append(assignments, a)
	}

	if assignments == nil {
		assignments = []TrainingAssignment{}
	}

	return assignments, nil
}
