package db

import (
	"database/sql"
	"fmt"
	"time"

	"git.leaktechnologies.dev/stu/Goondex/internal/model"
)

// MovieStore provides database operations for movies
type MovieStore struct {
	db *DB
}

// NewMovieStore creates a new MovieStore
func NewMovieStore(db *DB) *MovieStore {
	return &MovieStore{db: db}
}

// Create inserts a new movie into the database
func (s *MovieStore) Create(movie *model.Movie) error {
	now := time.Now()
	movie.CreatedAt = now
	movie.UpdatedAt = now

	result, err := s.db.conn.Exec(`
		INSERT INTO movies (
			title, date, studio_id, description, director, duration,
			image_path, image_url, back_image_url, url, source, source_id,
			created_at, updated_at
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
	`,
		movie.Title, movie.Date, movie.StudioID, movie.Description, movie.Director, movie.Duration,
		movie.ImagePath, movie.ImageURL, movie.BackImageURL, movie.URL, movie.Source, movie.SourceID,
		now, now,
	)
	if err != nil {
		return fmt.Errorf("failed to create movie: %w", err)
	}

	id, err := result.LastInsertId()
	if err != nil {
		return fmt.Errorf("failed to get movie ID: %w", err)
	}

	movie.ID = id
	return nil
}

// GetByID retrieves a movie by its ID
func (s *MovieStore) GetByID(id int64) (*model.Movie, error) {
	var movie model.Movie
	var studioID sql.NullInt64

	err := s.db.conn.QueryRow(`
		SELECT id, title, COALESCE(date, ''), COALESCE(studio_id, 0), COALESCE(description, ''),
			COALESCE(director, ''), COALESCE(duration, 0),
			COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(back_image_url, ''),
			COALESCE(url, ''), COALESCE(source, ''), COALESCE(source_id, ''),
			created_at, updated_at
		FROM movies
		WHERE id = ?
	`, id).Scan(
		&movie.ID, &movie.Title, &movie.Date, &studioID, &movie.Description,
		&movie.Director, &movie.Duration,
		&movie.ImagePath, &movie.ImageURL, &movie.BackImageURL,
		&movie.URL, &movie.Source, &movie.SourceID,
		&movie.CreatedAt, &movie.UpdatedAt,
	)

	if err == sql.ErrNoRows {
		return nil, fmt.Errorf("movie not found")
	}
	if err != nil {
		return nil, fmt.Errorf("failed to get movie: %w", err)
	}

	if studioID.Valid && studioID.Int64 > 0 {
		movie.StudioID = &studioID.Int64
	}

	return &movie, nil
}

// Search searches for movies by title
func (s *MovieStore) Search(query string) ([]model.Movie, error) {
	rows, err := s.db.conn.Query(`
		SELECT id, title, COALESCE(date, ''), COALESCE(studio_id, 0), COALESCE(description, ''),
			COALESCE(director, ''), COALESCE(duration, 0),
			COALESCE(image_path, ''), COALESCE(image_url, ''), COALESCE(back_image_url, ''),
			COALESCE(url, ''), COALESCE(source, ''), COALESCE(source_id, ''),
			created_at, updated_at
		FROM movies
		WHERE title LIKE ?
		ORDER BY date DESC, title ASC
		LIMIT 100
	`, "%"+query+"%")
	if err != nil {
		return nil, fmt.Errorf("failed to search movies: %w", err)
	}
	defer rows.Close()

	var movies []model.Movie
	for rows.Next() {
		var movie model.Movie
		var studioID sql.NullInt64

		if err := rows.Scan(
			&movie.ID, &movie.Title, &movie.Date, &studioID, &movie.Description,
			&movie.Director, &movie.Duration,
			&movie.ImagePath, &movie.ImageURL, &movie.BackImageURL,
			&movie.URL, &movie.Source, &movie.SourceID,
			&movie.CreatedAt, &movie.UpdatedAt,
		); err != nil {
			return nil, fmt.Errorf("failed to scan movie: %w", err)
		}

		if studioID.Valid && studioID.Int64 > 0 {
			movie.StudioID = &studioID.Int64
		}

		movies = append(movies, movie)
	}

	return movies, nil
}

// AddScene links a scene to a movie
func (s *MovieStore) AddScene(movieID, sceneID int64, sceneNumber int) error {
	_, err := s.db.conn.Exec(`
		INSERT OR IGNORE INTO movie_scenes (movie_id, scene_id, scene_number)
		VALUES (?, ?, ?)
	`, movieID, sceneID, sceneNumber)
	return err
}

// GetScenes returns all scenes for a movie
func (s *MovieStore) GetScenes(movieID int64) ([]model.Scene, error) {
	rows, err := s.db.conn.Query(`
		SELECT s.id, s.title, COALESCE(s.code, ''), COALESCE(s.date, ''), COALESCE(s.studio_id, 0),
			COALESCE(s.description, ''), COALESCE(s.image_path, ''), COALESCE(s.image_url, ''),
			COALESCE(s.director, ''), COALESCE(s.url, ''), COALESCE(s.source, ''), COALESCE(s.source_id, ''),
			s.created_at, s.updated_at, COALESCE(ms.scene_number, 0)
		FROM scenes s
		INNER JOIN movie_scenes ms ON s.id = ms.scene_id
		WHERE ms.movie_id = ?
		ORDER BY ms.scene_number ASC, s.title ASC
	`, movieID)
	if err != nil {
		return nil, fmt.Errorf("failed to get scenes: %w", err)
	}
	defer rows.Close()

	var scenes []model.Scene
	for rows.Next() {
		var scene model.Scene
		var studioID sql.NullInt64
		var sceneNumber int

		if err := rows.Scan(
			&scene.ID, &scene.Title, &scene.Code, &scene.Date, &studioID,
			&scene.Description, &scene.ImagePath, &scene.ImageURL,
			&scene.Director, &scene.URL, &scene.Source, &scene.SourceID,
			&scene.CreatedAt, &scene.UpdatedAt, &sceneNumber,
		); err != nil {
			return nil, fmt.Errorf("failed to scan scene: %w", err)
		}

		if studioID.Valid && studioID.Int64 > 0 {
			scene.StudioID = &studioID.Int64
		}

		scenes = append(scenes, scene)
	}

	return scenes, nil
}

// GetSceneCount returns the number of scenes in a movie
func (s *MovieStore) GetSceneCount(movieID int64) (int, error) {
	var count int
	err := s.db.conn.QueryRow(`
		SELECT COUNT(*) FROM movie_scenes WHERE movie_id = ?
	`, movieID).Scan(&count)
	return count, err
}

// Update updates an existing movie
func (s *MovieStore) Update(movie *model.Movie) error {
	movie.UpdatedAt = time.Now()

	_, err := s.db.conn.Exec(`
		UPDATE movies SET
			title = ?, date = ?, studio_id = ?, description = ?, director = ?, duration = ?,
			image_path = ?, image_url = ?, back_image_url = ?, url = ?, source = ?, source_id = ?,
			updated_at = ?
		WHERE id = ?
	`,
		movie.Title, movie.Date, movie.StudioID, movie.Description, movie.Director, movie.Duration,
		movie.ImagePath, movie.ImageURL, movie.BackImageURL, movie.URL, movie.Source, movie.SourceID,
		movie.UpdatedAt, movie.ID,
	)
	return err
}

// Delete removes a movie from the database
func (s *MovieStore) Delete(id int64) error {
	_, err := s.db.conn.Exec("DELETE FROM movies WHERE id = ?", id)
	return err
}
