package training

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

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

// CreateMedia creates a new media record
func (s *Store) CreateMedia(ctx context.Context, media *TrainingMedia) error {
	media.ID = uuid.New()
	media.CreatedAt = time.Now().UTC()
	media.UpdatedAt = media.CreatedAt
	if media.Metadata == nil {
		media.Metadata = json.RawMessage("{}")
	}

	_, err := s.pool.Exec(ctx, `
		INSERT INTO training_media (
			id, module_id, content_id, media_type, status,
			bucket, object_key, file_size_bytes, duration_seconds,
			mime_type, voice_model, language, metadata,
			error_message, generated_by, is_published, created_at, updated_at
		) VALUES (
			$1, $2, $3, $4, $5,
			$6, $7, $8, $9,
			$10, $11, $12, $13,
			$14, $15, $16, $17, $18
		)
	`,
		media.ID, media.ModuleID, media.ContentID, string(media.MediaType), string(media.Status),
		media.Bucket, media.ObjectKey, media.FileSizeBytes, media.DurationSeconds,
		media.MimeType, media.VoiceModel, media.Language, media.Metadata,
		media.ErrorMessage, media.GeneratedBy, media.IsPublished, media.CreatedAt, media.UpdatedAt,
	)

	return err
}

// GetMedia retrieves a media record by ID
func (s *Store) GetMedia(ctx context.Context, id uuid.UUID) (*TrainingMedia, error) {
	var media TrainingMedia
	var mediaType, status string

	err := s.pool.QueryRow(ctx, `
		SELECT id, module_id, content_id, media_type, status,
			bucket, object_key, file_size_bytes, duration_seconds,
			mime_type, voice_model, language, metadata,
			error_message, generated_by, is_published, created_at, updated_at
		FROM training_media WHERE id = $1
	`, id).Scan(
		&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
		&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
		&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
		&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
	)

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

	media.MediaType = MediaType(mediaType)
	media.Status = MediaStatus(status)
	return &media, nil
}

// GetMediaForModule retrieves all media for a module
func (s *Store) GetMediaForModule(ctx context.Context, moduleID uuid.UUID) ([]TrainingMedia, error) {
	rows, err := s.pool.Query(ctx, `
		SELECT id, module_id, content_id, media_type, status,
			bucket, object_key, file_size_bytes, duration_seconds,
			mime_type, voice_model, language, metadata,
			error_message, generated_by, is_published, created_at, updated_at
		FROM training_media WHERE module_id = $1
		ORDER BY media_type, created_at DESC
	`, moduleID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var mediaList []TrainingMedia
	for rows.Next() {
		var media TrainingMedia
		var mediaType, status string
		if err := rows.Scan(
			&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
			&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
			&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
			&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
		); err != nil {
			return nil, err
		}
		media.MediaType = MediaType(mediaType)
		media.Status = MediaStatus(status)
		mediaList = append(mediaList, media)
	}

	if mediaList == nil {
		mediaList = []TrainingMedia{}
	}
	return mediaList, nil
}

// UpdateMediaStatus updates the status and related fields of a media record
func (s *Store) UpdateMediaStatus(ctx context.Context, id uuid.UUID, status MediaStatus, sizeBytes int64, duration float64, errMsg string) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE training_media
		SET status = $2, file_size_bytes = $3, duration_seconds = $4,
			error_message = $5, updated_at = NOW()
		WHERE id = $1
	`, id, string(status), sizeBytes, duration, errMsg)
	return err
}

// PublishMedia publishes or unpublishes a media record
func (s *Store) PublishMedia(ctx context.Context, id uuid.UUID, publish bool) error {
	_, err := s.pool.Exec(ctx, `
		UPDATE training_media SET is_published = $2, updated_at = NOW() WHERE id = $1
	`, id, publish)
	return err
}

// GetPublishedAudio gets the published audio for a module
func (s *Store) GetPublishedAudio(ctx context.Context, moduleID uuid.UUID) (*TrainingMedia, error) {
	var media TrainingMedia
	var mediaType, status string

	err := s.pool.QueryRow(ctx, `
		SELECT id, module_id, content_id, media_type, status,
			bucket, object_key, file_size_bytes, duration_seconds,
			mime_type, voice_model, language, metadata,
			error_message, generated_by, is_published, created_at, updated_at
		FROM training_media
		WHERE module_id = $1 AND media_type = 'audio' AND is_published = true
		ORDER BY created_at DESC LIMIT 1
	`, moduleID).Scan(
		&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
		&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
		&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
		&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
	)

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

	media.MediaType = MediaType(mediaType)
	media.Status = MediaStatus(status)
	return &media, nil
}

// GetPublishedVideo gets the published video for a module
func (s *Store) GetPublishedVideo(ctx context.Context, moduleID uuid.UUID) (*TrainingMedia, error) {
	var media TrainingMedia
	var mediaType, status string

	err := s.pool.QueryRow(ctx, `
		SELECT id, module_id, content_id, media_type, status,
			bucket, object_key, file_size_bytes, duration_seconds,
			mime_type, voice_model, language, metadata,
			error_message, generated_by, is_published, created_at, updated_at
		FROM training_media
		WHERE module_id = $1 AND media_type = 'video' AND is_published = true
		ORDER BY created_at DESC LIMIT 1
	`, moduleID).Scan(
		&media.ID, &media.ModuleID, &media.ContentID, &mediaType, &status,
		&media.Bucket, &media.ObjectKey, &media.FileSizeBytes, &media.DurationSeconds,
		&media.MimeType, &media.VoiceModel, &media.Language, &media.Metadata,
		&media.ErrorMessage, &media.GeneratedBy, &media.IsPublished, &media.CreatedAt, &media.UpdatedAt,
	)

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

	media.MediaType = MediaType(mediaType)
	media.Status = MediaStatus(status)
	return &media, nil
}
