package training

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

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

// CreateModule creates a new training module
func (s *Store) CreateModule(ctx context.Context, module *TrainingModule) error {
	module.ID = uuid.New()
	module.CreatedAt = time.Now().UTC()
	module.UpdatedAt = module.CreatedAt
	if !module.IsActive {
		module.IsActive = true
	}

	isoControls, _ := json.Marshal(module.ISOControls)

	_, err := s.pool.Exec(ctx, `
		INSERT INTO training_modules (
			id, tenant_id, academy_course_id, module_code, title, description,
			regulation_area, nis2_relevant, iso_controls, frequency_type,
			validity_days, risk_weight, content_type, duration_minutes,
			pass_threshold, is_active, sort_order, created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5, $6,
			$7, $8, $9, $10,
			$11, $12, $13, $14,
			$15, $16, $17, $18, $19
		)
	`,
		module.ID, module.TenantID, module.AcademyCourseID, module.ModuleCode, module.Title, module.Description,
		string(module.RegulationArea), module.NIS2Relevant, isoControls, string(module.FrequencyType),
		module.ValidityDays, module.RiskWeight, module.ContentType, module.DurationMinutes,
		module.PassThreshold, module.IsActive, module.SortOrder, module.CreatedAt, module.UpdatedAt,
	)

	return err
}

// GetModule retrieves a module by ID
func (s *Store) GetModule(ctx context.Context, id uuid.UUID) (*TrainingModule, error) {
	var module TrainingModule
	var regulationArea, frequencyType string
	var isoControls []byte

	err := s.pool.QueryRow(ctx, `
		SELECT
			id, tenant_id, academy_course_id, module_code, title, description,
			regulation_area, nis2_relevant, iso_controls, frequency_type,
			validity_days, risk_weight, content_type, duration_minutes,
			pass_threshold, is_active, sort_order, created_at, updated_at
		FROM training_modules WHERE id = $1
	`, id).Scan(
		&module.ID, &module.TenantID, &module.AcademyCourseID, &module.ModuleCode, &module.Title, &module.Description,
		&regulationArea, &module.NIS2Relevant, &isoControls, &frequencyType,
		&module.ValidityDays, &module.RiskWeight, &module.ContentType, &module.DurationMinutes,
		&module.PassThreshold, &module.IsActive, &module.SortOrder, &module.CreatedAt, &module.UpdatedAt,
	)

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

	module.RegulationArea = RegulationArea(regulationArea)
	module.FrequencyType = FrequencyType(frequencyType)
	json.Unmarshal(isoControls, &module.ISOControls)
	if module.ISOControls == nil {
		module.ISOControls = []string{}
	}

	return &module, nil
}

// ListModules lists training modules for a tenant with optional filters
func (s *Store) ListModules(ctx context.Context, tenantID uuid.UUID, filters *ModuleFilters) ([]TrainingModule, int, error) {
	countQuery := "SELECT COUNT(*) FROM training_modules WHERE tenant_id = $1"
	countArgs := []interface{}{tenantID}
	countArgIdx := 2

	query := `
		SELECT
			id, tenant_id, academy_course_id, module_code, title, description,
			regulation_area, nis2_relevant, iso_controls, frequency_type,
			validity_days, risk_weight, content_type, duration_minutes,
			pass_threshold, is_active, sort_order, created_at, updated_at
		FROM training_modules WHERE tenant_id = $1`

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

	if filters != nil {
		if filters.RegulationArea != "" {
			query += fmt.Sprintf(" AND regulation_area = $%d", argIdx)
			args = append(args, string(filters.RegulationArea))
			argIdx++
			countQuery += fmt.Sprintf(" AND regulation_area = $%d", countArgIdx)
			countArgs = append(countArgs, string(filters.RegulationArea))
			countArgIdx++
		}
		if filters.FrequencyType != "" {
			query += fmt.Sprintf(" AND frequency_type = $%d", argIdx)
			args = append(args, string(filters.FrequencyType))
			argIdx++
			countQuery += fmt.Sprintf(" AND frequency_type = $%d", countArgIdx)
			countArgs = append(countArgs, string(filters.FrequencyType))
			countArgIdx++
		}
		if filters.IsActive != nil {
			query += fmt.Sprintf(" AND is_active = $%d", argIdx)
			args = append(args, *filters.IsActive)
			argIdx++
			countQuery += fmt.Sprintf(" AND is_active = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.IsActive)
			countArgIdx++
		}
		if filters.NIS2Relevant != nil {
			query += fmt.Sprintf(" AND nis2_relevant = $%d", argIdx)
			args = append(args, *filters.NIS2Relevant)
			argIdx++
			countQuery += fmt.Sprintf(" AND nis2_relevant = $%d", countArgIdx)
			countArgs = append(countArgs, *filters.NIS2Relevant)
			countArgIdx++
		}
		if filters.Search != "" {
			query += fmt.Sprintf(" AND (title ILIKE $%d OR description ILIKE $%d OR module_code ILIKE $%d)", argIdx, argIdx, argIdx)
			args = append(args, "%"+filters.Search+"%")
			argIdx++
			countQuery += fmt.Sprintf(" AND (title ILIKE $%d OR description ILIKE $%d OR module_code ILIKE $%d)", countArgIdx, countArgIdx, countArgIdx)
			countArgs = append(countArgs, "%"+filters.Search+"%")
			countArgIdx++
		}
	}

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

	query += " ORDER BY sort_order ASC, created_at DESC"

	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 modules []TrainingModule
	for rows.Next() {
		var module TrainingModule
		var regulationArea, frequencyType string
		var isoControls []byte

		err := rows.Scan(
			&module.ID, &module.TenantID, &module.AcademyCourseID, &module.ModuleCode, &module.Title, &module.Description,
			&regulationArea, &module.NIS2Relevant, &isoControls, &frequencyType,
			&module.ValidityDays, &module.RiskWeight, &module.ContentType, &module.DurationMinutes,
			&module.PassThreshold, &module.IsActive, &module.SortOrder, &module.CreatedAt, &module.UpdatedAt,
		)
		if err != nil {
			return nil, 0, err
		}

		module.RegulationArea = RegulationArea(regulationArea)
		module.FrequencyType = FrequencyType(frequencyType)
		json.Unmarshal(isoControls, &module.ISOControls)
		if module.ISOControls == nil {
			module.ISOControls = []string{}
		}

		modules = append(modules, module)
	}

	if modules == nil {
		modules = []TrainingModule{}
	}

	return modules, total, nil
}

// UpdateModule updates a training module
func (s *Store) UpdateModule(ctx context.Context, module *TrainingModule) error {
	module.UpdatedAt = time.Now().UTC()
	isoControls, _ := json.Marshal(module.ISOControls)

	_, err := s.pool.Exec(ctx, `
		UPDATE training_modules SET
			title = $2, description = $3, nis2_relevant = $4,
			iso_controls = $5, validity_days = $6, risk_weight = $7,
			duration_minutes = $8, pass_threshold = $9, is_active = $10,
			sort_order = $11, updated_at = $12
		WHERE id = $1
	`,
		module.ID, module.Title, module.Description, module.NIS2Relevant,
		isoControls, module.ValidityDays, module.RiskWeight,
		module.DurationMinutes, module.PassThreshold, module.IsActive,
		module.SortOrder, module.UpdatedAt,
	)

	return err
}

// DeleteModule deletes a training module by ID
func (s *Store) DeleteModule(ctx context.Context, id uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `DELETE FROM training_modules WHERE id = $1`, id)
	return err
}

// SetAcademyCourseID links a training module to an academy course
func (s *Store) SetAcademyCourseID(ctx context.Context, moduleID, courseID uuid.UUID) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE training_modules SET academy_course_id = $2, updated_at = $3 WHERE id = $1
	`, moduleID, courseID, time.Now().UTC())
	return err
}
